okstra 0.97.1 → 0.98.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/README.kr.md +4 -3
- package/README.md +4 -3
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/cli.md +2 -0
- package/docs/kr/container.md +122 -0
- package/docs/project-structure-overview.md +12 -2
- package/docs/superpowers/plans/2026-06-21-okstra-container-local-user-test.md +714 -0
- package/docs/superpowers/specs/2026-06-21-okstra-container-local-user-test-design.md +125 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -18
- package/runtime/agents/workers/claude-worker.md +10 -33
- package/runtime/agents/workers/codex-worker.md +2 -18
- package/runtime/agents/workers/report-writer-worker.md +2 -10
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +5 -2
- package/runtime/bin/okstra-antigravity-exec.sh +6 -1
- package/runtime/bin/okstra-claude-exec.sh +6 -1
- package/runtime/bin/okstra-codex-exec.sh +6 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +1 -0
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +17 -0
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -2
- package/runtime/prompts/profiles/_implementation-self-check.md +2 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/_stage-discipline.md +5 -8
- package/runtime/prompts/profiles/error-analysis.md +5 -5
- package/runtime/prompts/profiles/final-verification.md +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +7 -7
- package/runtime/prompts/profiles/implementation.md +5 -5
- package/runtime/prompts/profiles/improvement-discovery.md +6 -6
- package/runtime/prompts/profiles/release-handoff.md +3 -3
- package/runtime/prompts/profiles/requirements-discovery.md +4 -4
- package/runtime/prompts/wizard/prompts.ko.json +0 -1
- package/runtime/python/okstra_ctl/__init__.py +4 -1
- package/runtime/python/okstra_ctl/container.py +970 -0
- package/runtime/python/okstra_ctl/container_registry.py +93 -0
- package/runtime/python/okstra_ctl/error_zip.py +4 -4
- package/runtime/python/okstra_ctl/handoff.py +7 -1
- package/runtime/python/okstra_ctl/ids.py +40 -1
- package/runtime/python/okstra_ctl/listing.py +3 -5
- package/runtime/python/okstra_ctl/log_report.py +102 -0
- package/runtime/python/okstra_ctl/pane_reclaim.py +5 -4
- package/runtime/python/okstra_ctl/paths.py +40 -0
- package/runtime/python/okstra_ctl/plan_run_root.py +78 -0
- package/runtime/python/okstra_ctl/reconcile.py +4 -2
- package/runtime/python/okstra_ctl/resolve_task_key.py +54 -0
- package/runtime/python/okstra_ctl/run.py +48 -30
- package/runtime/python/okstra_ctl/stage_integrate.py +8 -2
- package/runtime/python/okstra_ctl/stage_targets.py +79 -0
- package/runtime/python/okstra_ctl/time_report.py +200 -0
- package/runtime/python/okstra_ctl/tmux.py +67 -0
- package/runtime/python/okstra_ctl/wizard.py +35 -20
- package/runtime/python/okstra_project/__init__.py +2 -0
- package/runtime/python/okstra_project/state.py +50 -2
- package/runtime/skills/okstra-brief/SKILL.md +17 -7
- package/runtime/skills/okstra-container/SKILL.md +169 -0
- package/runtime/skills/okstra-inspect/SKILL.md +64 -178
- package/runtime/skills/okstra-memory/SKILL.md +8 -6
- package/runtime/skills/okstra-run/SKILL.md +4 -4
- package/runtime/skills/okstra-schedule/SKILL.md +9 -16
- package/runtime/skills/okstra-setup/SKILL.md +10 -9
- package/runtime/templates/reports/brief.template.md +1 -1
- package/runtime/templates/reports/final-report.template.md +1 -1
- package/runtime/templates/reports/settings.template.json +3 -1
- package/runtime/validators/validate-implementation-plan-stages.py +11 -3
- package/src/cli-registry.mjs +28 -0
- package/src/commands/inspect/container.mjs +27 -0
- package/src/commands/inspect/log-report.mjs +26 -0
- package/src/commands/inspect/resolve-task-key.mjs +26 -0
- package/src/commands/inspect/task-list.mjs +26 -15
- package/src/commands/inspect/time-report.mjs +26 -0
- package/src/commands/lifecycle/check-project.mjs +7 -1
- package/src/commands/lifecycle/doctor.mjs +16 -0
- package/src/lib/skill-catalog.mjs +1 -0
- package/runtime/agents/TODO.md +0 -226
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# okstra-container — 로컬 유저-테스트 컨테이너 오케스트레이션 설계
|
|
2
|
+
|
|
3
|
+
- 상태: 초안 (사용자 검토 대기)
|
|
4
|
+
- 작성일: 2026-06-21
|
|
5
|
+
- 관련: [stage worktree 격리](2026-06-06-stage-worktree-isolation-design.md), [stage 자동 통합/teardown](2026-06-20-stage-auto-integrate-teardown-design.md)
|
|
6
|
+
|
|
7
|
+
## 1. 배경 & 목적
|
|
8
|
+
|
|
9
|
+
okstra-run 의 phase 들(특히 `final-verification`)이 검증한 코드를 **개발자 로컬에서 유저-테스트 환경으로 띄워 직접 확인**하고 싶다는 요구가 있다. 대상 프로젝트는 자체 `Dockerfile`/`docker-compose.yml`/`.env` 같은 컨테이너 설정을 이미 갖고 있는 경우가 많다.
|
|
10
|
+
|
|
11
|
+
이 설계는 그 설정을 **재사용**해 특정 task-id 의 코드를 **task 별 컨테이너 그룹**으로 띄우고, 각 컨테이너의 로그를 **상시 감시(watcher)** 하는 새 스킬 패밀리 `okstra-container` 를 정의한다.
|
|
12
|
+
|
|
13
|
+
핵심 제약(브레인스토밍 합의):
|
|
14
|
+
|
|
15
|
+
- **오케스트레이션형(B):** 설정 파일을 **생성하지 않는다.** 기존 파일을 재사용할 뿐이며, 없으면 **무엇이 없는지 명시하고 즉시 중단**한다.
|
|
16
|
+
- **비선형:** PHASE_SEQUENCE 의 phase 가 아니다. 임의의 task-id 를 검증 통과 여부와 무관하게 띄울 수 있다.
|
|
17
|
+
- **artifact-home 준수:** okstra 산출물은 전부 `<PROJECT_ROOT>/.okstra/` 아래에 둔다. 원본 프로젝트 파일은 변경하지 않는다. 컨테이너 실행 자체는 외부 부수효과이므로 docker 라벨 + 레지스트리로 추적한다.
|
|
18
|
+
|
|
19
|
+
## 2. 정체성 & 경계
|
|
20
|
+
|
|
21
|
+
`okstra-container` 는 `okstra-inspect` 와 같은 **sub-command 디스패치 스킬**이다. `okstra-run`/PHASE_SEQUENCE 와 독립적이며, task-id 로 주소지정한다.
|
|
22
|
+
|
|
23
|
+
| 한다 | 안 한다 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| 기존 `docker-compose.yml`/`Dockerfile`/`.env` 재사용해 배포 | 설정 파일 자동 생성 |
|
|
26
|
+
| task-id 별 컨테이너 그룹핑·라벨링 | 원격/CI 배포 |
|
|
27
|
+
| 컨테이너별 tail pane + watcher 에이전트 상시 감시 | 에러 자동 치유(재시작 등) |
|
|
28
|
+
| 사후 status/logs/teardown | PHASE_SEQUENCE phase 편입 |
|
|
29
|
+
|
|
30
|
+
## 3. 스킬 sub-command 표면
|
|
31
|
+
|
|
32
|
+
| sub-command | 동작 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `up <task-id>` | stage 통합 확인 → 설정 파일 검증 → override `.env` 합성 → `docker compose up` → tail pane + watcher 스폰 |
|
|
35
|
+
| `status [task-id]` | 살아있는 컨테이너 그룹 / watcher 현황 (docker 라벨 쿼리) |
|
|
36
|
+
| `logs <task-id> [service]` | 사후 로그 조회 |
|
|
37
|
+
| `stop-watcher <task-id> <service>` | 해당 watcher 에이전트 + tail pane 만 종료 |
|
|
38
|
+
| `down <task-id> [service\|all]` | 컨테이너(+딸린 watcher) teardown |
|
|
39
|
+
|
|
40
|
+
자연어 요청("db watcher 꺼줘")은 스킬이 위 sub-command 로 매핑한다.
|
|
41
|
+
|
|
42
|
+
## 4. `up` 플로우 (executor 역할 — 결정론적)
|
|
43
|
+
|
|
44
|
+
executor 는 **상시 프로세스가 아니라 논리적 역할**이다. `up` 시점에 결정론적으로 다음을 수행한다.
|
|
45
|
+
|
|
46
|
+
1. **소스 트리 해소** — task-key worktree HEAD (`~/.okstra/worktrees/<project-id>/<task-group>/<task-id>/`).
|
|
47
|
+
- 미통합 stage 발견 시: 사용자에게 알리고 `final-verification` whole-task 통합 로직([run.py:1485-1543](../../../runtime/python/okstra_ctl/run.py))을 재사용해 머지한다.
|
|
48
|
+
- 머지 충돌 시: 중단하고 충돌 내용을 안내한다.
|
|
49
|
+
2. **설정 파일 검증** — worktree 루트에 `docker-compose.yml`(필요 시 `Dockerfile`) 존재 확인. **없으면 무엇이 없는지 명시하고 중단.**
|
|
50
|
+
3. **override `.env` 합성** — 프로젝트 `.env` 위에 task 별 override(`§6` 레이아웃)를 얹는다.
|
|
51
|
+
4. **배포** — `docker compose -p <project-name> --env-file <project .env> --env-file <override> up -d`, okstra 라벨 부착.
|
|
52
|
+
5. **헬스체크** — `docker compose ps` / healthcheck 폴링으로 기동을 확인.
|
|
53
|
+
6. **감시 스폰** — 기동된 컨테이너마다 ① tail pane(`docker compose -p <project-name> logs -f <service>`) ② watcher 에이전트 1개.
|
|
54
|
+
|
|
55
|
+
## 5. 지속성 & 제어 모델
|
|
56
|
+
|
|
57
|
+
- **그릇:** task-id 당 **detached tmux 세션**(`okstra-container-<task-id>`). Claude 세션이 끝나도 생존한다. okstra 의 기존 detached-tmux 패턴([cmd-rerun.sh](../../../scripts/lib/okstra-ctl/cmd-rerun.sh)) + pane trace 태그([okstra-trace-cleanup.sh](../../../scripts/okstra-trace-cleanup.sh))를 재사용한다.
|
|
58
|
+
- **executor = 논리적 역할.** 컨테이너 수명은 docker 데몬이 보장하므로 executor 프로세스가 살아있을 필요가 없다. "executor 종료" = `down`.
|
|
59
|
+
- **watcher = 살아있는 AI 에이전트.** 컨테이너당 1개. **에러 트리거 2단계 모델**로 동작한다: ① 경량 스캔 루프 — `docker compose logs --since <last>` 를 `scan_interval_seconds`(주입 인자) 간격으로 가져와 정규식 패턴(`ERROR`/`FATAL`/`Exception`/`Traceback`/비정상 exit code 등)만 매칭, 매칭 0건이면 **LLM 호출 없이** 다음 주기. ② 심층 분석 — 패턴 감지 시에만 해당 로그 윈도우를 watcher AI 에이전트(codex/antigravity-worker 와 동일 pane 메커니즘)에게 넘겨 `findings.md` 에 기록. 동일 에러 시그니처는 디바운스. **사용자가 끌 때까지 지속.** watcher 는 **탐지·보고만**(§9) — findings append 외 코드/설정 쓰기 금지(read-only 보고 계약).
|
|
60
|
+
- watcher/tail pane 은 `@okstra_trace_run`/`@okstra_worker_run` **둘 다 붙이지 않고** 신규 `@okstra_container_run` 태그만 사용한다. SessionEnd `--reap` 이 Claude 종료 시 watcher 를 죽이지 않게 하기 위함(이 둘 중 하나라도 붙으면 reap 대상이 됨).
|
|
61
|
+
- **monitoring 수명:** 유한 윈도우가 아니라 **상시 지속**. 종료는 사용자 명령으로만 일어난다.
|
|
62
|
+
- **종료 의미:**
|
|
63
|
+
- `stop-watcher <service>` → 해당 watcher pane 만 종료(컨테이너는 유지).
|
|
64
|
+
- `down <service>` → 컨테이너 + 딸린 watcher 종료.
|
|
65
|
+
- `down all` → 세션 전체 teardown + trace-cleanup.
|
|
66
|
+
|
|
67
|
+
## 6. 변수 주입 & artifact-home 레이아웃
|
|
68
|
+
|
|
69
|
+
원본 프로젝트 파일은 불변. okstra 산출물은 task 하위에 둔다.
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
.okstra/tasks/<group>/<task-id>/container/
|
|
73
|
+
├── env.override # task 별 주입 변수 (프로젝트 .env 위에 레이어)
|
|
74
|
+
├── registry.json # 이 task 의 그룹/세션/watcher 상태 (flock-guarded)
|
|
75
|
+
├── deploy-state.json # compose project name, 기동 컨테이너, 라벨
|
|
76
|
+
└── watchers/<service>-findings.md # watcher 별 에러 분석 로그
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
compose env 우선순위: `--env-file <worktree .env>` 다음에 `--env-file env.override` (나중에 지정된 것이 동일 키를 덮어쓴다). 따라서 포트/접속정보/모킹 전환 같은 유저-테스트 전용 값은 `env.override` 에만 두고 원본 `.env` 는 건드리지 않는다.
|
|
80
|
+
|
|
81
|
+
**입력 `.env` 출처(artifact-home 게이트):** 첫 `--env-file` 의 `.env` 는 **worktree(okstra 소유, `~/.okstra/worktrees/...`)** 기준으로 해소한다 — 원본 프로젝트 루트의 `.env` 를 직접 참조하지 않는다. worktree 는 `worktree.py` 의 sync/snapshot 으로 MAIN 의 `.env` 를 이미 전달받으므로 출처가 okstra 소유 트리로 일관된다. `up` 의 cwd 도 worktree 임을 게이트로 단언한다.
|
|
82
|
+
|
|
83
|
+
**bind mount/volume 탈출(경고 후 진행):** `up` 전 `docker-compose.yml` 의 bind mount/volume 경로를 스캔해 worktree 밖(`../` 탈출, 절대 호스트 경로)을 가리키는 host path 가 있으면 **어떤 경로인지 경고로 명시하되 중단하지 않고 진행**한다(사용자 책임). 이는 의도된 결정이며, 컨테이너가 호스트에 파일을 생성할 수 있음을 사용자에게 알리는 데 목적이 있다.
|
|
84
|
+
|
|
85
|
+
## 7. 네이밍 & 상태 추적 (SSOT)
|
|
86
|
+
|
|
87
|
+
- **compose project name:** docker-safe(`[a-z0-9_-]`) — `okstra-<project-id-seg>-<group-seg>-<task-id-seg>`. 길이 초과 시 task-id 세그먼트를 우선 보존하며 truncation.
|
|
88
|
+
- **SSOT = docker 라벨.** 모든 컨테이너에 `okstra.task-key`, `okstra.project-name`, `okstra.run-trace` 라벨을 부착한다. `status`/`down` 은 라벨 쿼리로 live 그룹을 찾는다.
|
|
89
|
+
- `registry.json` 은 docker 가 모르는 정보(tmux 세션명, watcher pane-id, findings 경로)만 보관한다. 즉 컨테이너 존재 여부의 SSOT 는 항상 docker 이고, registry 는 보조 인덱스다.
|
|
90
|
+
|
|
91
|
+
## 8. 단일 참조점 & 등록 지점
|
|
92
|
+
|
|
93
|
+
- **수렴점:** 스킬 / `bin/okstra` CLI / `okstra.sh` 진입은 모두 새 python entrypoint(예: `scripts/okstra_ctl/container.py` 의 `provision_container_group()`)로 수렴한다 — `prepare_task_bundle` 단일 참조 규칙과 동일. 한 진입점을 다른 진입점이 래핑하지 않는다.
|
|
94
|
+
- **PHASE_SEQUENCE / workflow / wizard 의 task-types 는 건드리지 않는다**(phase 가 아니므로).
|
|
95
|
+
- **새로 손볼 곳:**
|
|
96
|
+
- `skills/okstra-container/SKILL.md` 신규 (host 기준 실행/Read 경로 사용 — `~/.okstra/...`).
|
|
97
|
+
- install 동기화([install.mjs](../../../src/commands/lifecycle/install.mjs)) + `installed-skills.json` 스킬 카운트.
|
|
98
|
+
- [doctor](../../../src/commands/lifecycle/doctor.mjs) 헬스체크에 `okstra-container` + docker 가용성 점검 추가.
|
|
99
|
+
- [settings.template.json](../../../templates/reports/settings.template.json) 에 `Bash(docker compose:*)` / `Bash(docker:*)` 권한 시드 추가 (end-user 전파).
|
|
100
|
+
- python 로직은 `scripts/okstra_ctl/` 와 `runtime/python/okstra_ctl/` **두 미러 트리**를 동기 수정 후 `npm run build`.
|
|
101
|
+
- 테스트: `tests/<domain>/` 에 단위 테스트(설정 파일 부재 시 중단, project name truncation, env 레이어 우선순위, 라벨 쿼리), `tests-e2e/scenario-<id>-okstra-container-*.sh` 1종.
|
|
102
|
+
|
|
103
|
+
## 9. 범위 밖 (YAGNI / v1 제외)
|
|
104
|
+
|
|
105
|
+
- 설정 파일 자동 생성 — 향후 sidetrack worker 로 분리.
|
|
106
|
+
- 다중 task-id 동시 오케스트레이션 UI, 원격/CI 배포.
|
|
107
|
+
- watcher 의 자동 치유(에러 발견 시 자동 재시작 등) — v1 은 **탐지·보고만**.
|
|
108
|
+
|
|
109
|
+
## 10. 확정된 결정 (브레인스토밍 + 계획 리뷰)
|
|
110
|
+
|
|
111
|
+
브레인스토밍 미해결 4건과 적대적 리뷰가 제기한 항목을 모두 확정한다.
|
|
112
|
+
|
|
113
|
+
- **watcher 토큰 예산** — 에러 트리거 2단계(§5). 정상 구간 LLM 호출 0, 에러 감지 시에만 심층 분석 + 동일 시그니처 디바운스.
|
|
114
|
+
- **healthcheck** — `healthcheck_timeout_seconds`(기본 120), `healthcheck_interval_seconds`(기본 3) 를 `provision_container_group()`/`up` 의 named 인자로 노출(env 금지). `docker compose ps --format json` 폴링, timeout 초과 시 어떤 서비스가 미기동인지 명시하고 중단. healthcheck 미정의 서비스는 `State=running` 도달을 종료조건으로 간주.
|
|
115
|
+
- **Dockerfile 단독** — `docker-compose.yml` 부재 시 무엇이 없는지 명시하고 중단. 단일 컨테이너 fallback 미허용(생성 안 함 원칙·KISS).
|
|
116
|
+
- **tmux 부재 degrade** — `compose up`(+healthcheck)까지 수행하되 tail/watcher pane 만 skip 하고 "감시 비활성(tmux 없음)" 을 결과에 명시. detached 세션 미생성. tmux 가용성 판정은 기존 `tmux_available()`([tmux.py:42](../../../scripts/okstra_ctl/tmux.py)) 단일 참조점으로 수렴(`process.env.TMUX` 판정 폐기 — 이는 "tmux 세션 내부" 신호이지 "tmux 설치" 신호가 아님).
|
|
117
|
+
- **bind mount 탈출** — 경고 후 진행(§6).
|
|
118
|
+
- **stage 트리** — 컨테이너 `up` 의 stage 머지는 `teardown=False`(머지만, stage worktree 보존). final-verification 의 `teardown=True` 와 분기.
|
|
119
|
+
- **doctor docker 점검** — `results` 배열에 넣지 않고 `runtimeResolution` 처럼 별도 WARN 으로 출력(`allOk` 비반영). docker 미설치 사용자가 `exit 1` 로 회귀하지 않게 함. `okstra-container` 는 `REQUIRED_SKILLS` 에 넣지 않음(비필수).
|
|
120
|
+
|
|
121
|
+
## 11. 구현 시 확정할 잔여 항목
|
|
122
|
+
|
|
123
|
+
- `plan_run_root`/`done_rows` 역추적 resolver — container 는 `approved_plan_path` 입력이 없어 task-id 로 implementation-planning run-root 를 역추적해야 한다. 같은 task 에 planning run 이 여럿이면 어느 run 의 `consumers.jsonl` 이 머지 SSOT 인지 선택 규칙(예: 최신 done 보유 run)을 정하고, 모호하면 중단해 사용자에게 run-root 를 명시 요구.
|
|
124
|
+
- `down`/`stop-watcher` orphan reconciliation — registry(보조 인덱스)가 비어도 살아있는 watcher pane 이 있을 수 있으므로, teardown 경로에서 docker 라벨 쿼리와 별개로 `@okstra_container_run` 태그/세션을 tmux 실측 스캔해 orphan 을 회수.
|
|
125
|
+
- compose project name truncation — 빈 슬러그/연속 하이픈/전부-비영숫자 세그먼트에서도 `^[a-z0-9][a-z0-9_-]*$` 를 만족하도록 형식 보장. 세그먼트 정규화는 `task_dir` 의 slug 함수와 동일 함수로 통일(라벨↔경로 일치 보장).
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -112,7 +112,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
|
|
|
112
112
|
```
|
|
113
113
|
Emit that line first, then the stdout unmodified. The model line is the ONLY addition permitted — do not otherwise summarize or alter the CLI output. This applies to convergence reverify dispatches too.
|
|
114
114
|
|
|
115
|
-
9. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/
|
|
115
|
+
9. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate_improvement_report.py`.
|
|
116
116
|
|
|
117
117
|
## Stop Condition
|
|
118
118
|
|
|
@@ -154,23 +154,7 @@ The CLI writes a Reading Confirmation block to the **audit sidecar** at `runs/<t
|
|
|
154
154
|
|
|
155
155
|
## Worker Output Structure
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
**Frontmatter (mandatory)** — set `workerId: "antigravity"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the primary input (`analysis-packet.md`; fall back to `analysis-material.md` only if the packet is missing a field). Full schema and a concrete example live in the team-contract resource (`prompts/lead/team-contract.md`) "Result Frontmatter" subsection.
|
|
160
|
-
|
|
161
|
-
1. **Findings** - what Antigravity identified
|
|
162
|
-
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
163
|
-
3. **Safe or Reasonable Areas** - parts that look correct
|
|
164
|
-
4. **Uncertain Points** - areas needing further review
|
|
165
|
-
5. **Recommended Next Actions** - suggested follow-ups
|
|
166
|
-
|
|
167
|
-
Include file paths and line numbers when discussing code evidence.
|
|
168
|
-
|
|
169
|
-
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Antigravity may use `F-1`, `F-2`, ... or numbered hierarchical IDs — either is fine. What matters is that each item is addressable. The lead's §6.1 / §6.2 / §2.1 synthesis preserves these IDs as `antigravity:<your-id>` entries in its `Source items (worker:item)` column. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
170
|
-
|
|
171
|
-
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the team-contract resource (`prompts/lead/team-contract.md`) Ticket Tagging section.
|
|
172
|
-
|
|
173
|
-
This contract mirrors the team-contract resource (`prompts/lead/team-contract.md`) Worker Output Contract — that resource is the authoritative source if the two ever diverge.
|
|
157
|
+
The Antigravity CLI — not this wrapper — produces the worker result. Its required structure is defined in the preamble (the lead injects the `**Worker Preamble Path:**` anchor into the dispatched prompt you persist and forward, and the CLI Reads it) plus the team-contract resource (`prompts/lead/team-contract.md`) "Worker Output Contract": YAML frontmatter with `workerId: "antigravity"`, sections 1–5 (common core) plus optional Section 6, a unique item ID on every item, and ticket tagging on `requirements-discovery` / `error-analysis` / `implementation-planning` / `implementation` runs. This wrapper forwards the CLI's output unmodified except for the single `**Model:**` line (step 8d) — never reshape or summarize it.
|
|
174
158
|
|
|
175
159
|
## Error reporting
|
|
176
160
|
|
|
@@ -51,7 +51,7 @@ Unlike the Codex / Antigravity workers, you are an in-process Claude subagent
|
|
|
51
51
|
|
|
52
52
|
5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
|
|
53
53
|
|
|
54
|
-
6. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/
|
|
54
|
+
6. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate_improvement_report.py`.
|
|
55
55
|
|
|
56
56
|
## Required Reading Before Any Analysis
|
|
57
57
|
|
|
@@ -60,27 +60,17 @@ Before producing any output, you MUST:
|
|
|
60
60
|
1. Extract the absolute path from the lead's `**Worker Preamble Path:**` anchor header and Read that file end-to-end with a single `Read` call (no `offset`, no `limit`). This is the canonical SSOT for the Required Reading + Error Reporting + Output sections contract.
|
|
61
61
|
2. Read every primary input file the lead enumerated under `## Inputs` (or equivalent heading) in the dispatch prompt body, end-to-end, following the rules stated in the preamble. For analysis workers this is normally `analysis-packet.md`; the source files named inside that packet are fallback/evidence paths to open when needed. Analysis workers do NOT read `final-report-template.md` — that file is for the report writer only.
|
|
62
62
|
|
|
63
|
-
**Heartbeat — write the audit sidecar EARLY and APPEND per stage (BLOCKING).**
|
|
63
|
+
**Heartbeat — write the audit sidecar EARLY and APPEND per stage (BLOCKING).** This worker runs as an in-process Agent or a fresh-session tmux pane, so the lead has no `BashOutput`-style liveness signal while it waits for your return — the audit sidecar is the only signal that survives a silent hang.
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
1. **Findings** - what you identified
|
|
72
|
-
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
73
|
-
3. **Safe or Reasonable Areas** - parts that look correct
|
|
74
|
-
4. **Uncertain Points** - areas needing further review
|
|
75
|
-
5. **Recommended Next Actions** - suggested follow-ups
|
|
65
|
+
- **Where:** `runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`.
|
|
66
|
+
- **Start (before the per-file reads):** immediately after extracting `Project Root` and the assigned paths, `Write` just the heading line (`# Claude Worker Audit — <task-key>`) plus one `- PROGRESS: started <ISO-8601-UTC>` line.
|
|
67
|
+
- **Append per stage** (`Edit` or heredoc `>>`): `read-<filename>`, `analysis-start`, `findings-draft-start`, `findings-draft-complete`, `write-result-start`.
|
|
68
|
+
- **Cadence MUST NOT exceed 5 minutes:** if a single analysis stage runs longer, emit a `- PROGRESS: in-stage:<stage> <ISO-8601-UTC>` line. A 5-minute-stale sidecar mtime is the canonical "this worker has hung" signal for the operator.
|
|
69
|
+
- **Enforcement:** the Phase 7 validator (`validate_session_conformance.py`) parses these `- PROGRESS:` lines post-hoc and fails the run when the first stage is not `started`, `write-result-start` is missing despite an existing result file, timestamps regress/unparse, or consecutive lines are more than 5 minutes (+60s grace) apart.
|
|
76
70
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Use the leading column for table-form items (`F-001`, `M-001`, `S-001`, `U-001`, `R-001` per section) or a `[<ID>]` prefix for bullet/numbered items. The ID shape is your choice but it MUST appear — the lead's §6.1 / §6.2 / §2.1 synthesis preserves these IDs in its `Source items (worker:item)` column to keep cross-worker traceability intact. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
80
|
-
|
|
81
|
-
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the team-contract resource (`prompts/lead/team-contract.md`) Ticket Tagging section.
|
|
71
|
+
## Worker Output Structure
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
Follow the **Worker output sections** contract in the preamble you Read via the `**Worker Preamble Path:**` anchor: YAML frontmatter, then sections 1–5 (common core) plus optional Section 6 (your specialization lens), every item carrying a unique worker-internal item ID, with ticket tagging on `requirements-discovery` / `error-analysis` / `implementation-planning` / `implementation` runs. Set `workerId: "claude"`. The full frontmatter schema, item-ID conventions, and authoritative section ordering live in the team-contract resource (`prompts/lead/team-contract.md`) "Worker Output Contract" — the SSOT if anything is ambiguous.
|
|
84
74
|
|
|
85
75
|
## Stop Condition (BLOCKING)
|
|
86
76
|
|
|
@@ -102,25 +92,12 @@ If you find yourself thinking "let me double-check section 3" or "I should read
|
|
|
102
92
|
|
|
103
93
|
## Error reporting
|
|
104
94
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
**Path extraction (BLOCKING).** Before recording anything, extract the absolute sidecar path from the lead's dispatch prompt body:
|
|
108
|
-
|
|
109
|
-
- `**Errors sidecar path:** <abs-path>` — this worker's per-run sidecar JSON.
|
|
110
|
-
|
|
111
|
-
If the header line is absent from the dispatch prompt, return `CLAUDE_WORKER_ERRORS_PATH_MISSING: lead prompt did not include **Errors sidecar path:** header` without proceeding. Do NOT synthesize the path from `runs/<task-type>/...` — that template syntax is documentation only and historically led to silent log loss.
|
|
112
|
-
|
|
113
|
-
**Tool failure (worker-reported)** — if `Write` of the prompt history file, `mkdir`, any MCP call, or any other pre-/in-analysis tool call fails (non-zero exit, exception, or empty result where data was required), append a `tool-failure` entry to the worker errors sidecar at the absolute path extracted from the `**Errors sidecar path:**` header. If the file does not exist, create it with `{"schemaVersion": 1, "errors": []}` then append.
|
|
114
|
-
|
|
115
|
-
The sidecar follows the schema in `prompts/lead/team-contract.md` (Optional errors sidecar). Lead will dump it to the run error log after this subagent terminates.
|
|
116
|
-
|
|
117
|
-
There is NO `cli-failure` category for this worker — Claude worker has no external CLI to invoke. Treat MCP errors and Bash errors uniformly as `tool-failure`.
|
|
95
|
+
Record your own tool failures per the **Error reporting** contract in the preamble: extract the `**Errors sidecar path:**` from the dispatch prompt (return `CLAUDE_WORKER_ERRORS_PATH_MISSING` without proceeding if it is absent — do NOT synthesize a `runs/...` path), then append a `tool-failure` entry there for any failed pre-/in-analysis tool call (creating the file with `{"schemaVersion": 1, "errors": []}` if missing). The lead dumps the sidecar to the run error log after this subagent terminates. This worker has no external CLI, so there is no `cli-failure` category — treat MCP and Bash errors uniformly as `tool-failure`.
|
|
118
96
|
|
|
119
97
|
## Notes
|
|
120
98
|
|
|
121
99
|
- Return error messages as-is on failure.
|
|
122
100
|
- Do not summarize or modify your own analysis output beyond the structured sections above.
|
|
123
|
-
- Sections 1–5 are the common core — same dimensions for every analysis worker. Your specialization (broad reasoning depth, hidden-assumption surfacing, execution-risk decomposition) only enters Section 6 if you have additive content beyond the core. See `prompts/lead/team-contract.md` "Worker Output Contract" for the authoritative split.
|
|
124
101
|
|
|
125
102
|
## Stage evidence emission (BLOCKING, implementation task only)
|
|
126
103
|
|
|
@@ -112,7 +112,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
|
|
|
112
112
|
```
|
|
113
113
|
Emit that line first, then the stdout unmodified. The model line is the ONLY addition permitted — do not otherwise summarize or alter the CLI output. This applies to convergence reverify dispatches too.
|
|
114
114
|
|
|
115
|
-
9. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/
|
|
115
|
+
9. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate_improvement_report.py`.
|
|
116
116
|
|
|
117
117
|
## Stop Condition
|
|
118
118
|
|
|
@@ -154,23 +154,7 @@ The CLI writes a Reading Confirmation block to the **audit sidecar** at `runs/<t
|
|
|
154
154
|
|
|
155
155
|
## Worker Output Structure
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
**Frontmatter (mandatory)** — set `workerId: "codex"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the primary input (`analysis-packet.md`; fall back to `analysis-material.md` only if the packet is missing a field). Full schema and a concrete example live in the team-contract resource (`prompts/lead/team-contract.md`) "Result Frontmatter" subsection.
|
|
160
|
-
|
|
161
|
-
1. **Findings** - what Codex identified
|
|
162
|
-
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
163
|
-
3. **Safe or Reasonable Areas** - parts that look correct
|
|
164
|
-
4. **Uncertain Points** - areas needing further review
|
|
165
|
-
5. **Recommended Next Actions** - suggested follow-ups
|
|
166
|
-
|
|
167
|
-
Include file paths and line numbers when discussing code evidence.
|
|
168
|
-
|
|
169
|
-
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Codex tends to use hierarchical numbering (`1.1`, `1.2`, `1.3`, ...); that shape is fine — keep what's natural. What matters is that each item is addressable. The lead's §6.1 / §6.2 / §2.1 synthesis preserves these IDs as `codex:<your-id>` entries in its `Source items (worker:item)` column. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
170
|
-
|
|
171
|
-
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the team-contract resource (`prompts/lead/team-contract.md`) Ticket Tagging section.
|
|
172
|
-
|
|
173
|
-
This contract mirrors the team-contract resource (`prompts/lead/team-contract.md`) Worker Output Contract — that resource is the authoritative source if the two ever diverge.
|
|
157
|
+
The Codex CLI — not this wrapper — produces the worker result. Its required structure is defined in the preamble (the lead injects the `**Worker Preamble Path:**` anchor into the dispatched prompt you persist and forward, and the CLI Reads it) plus the team-contract resource (`prompts/lead/team-contract.md`) "Worker Output Contract": YAML frontmatter with `workerId: "codex"`, sections 1–5 (common core) plus optional Section 6, a unique item ID on every item, and ticket tagging on `requirements-discovery` / `error-analysis` / `implementation-planning` / `implementation` runs. This wrapper forwards the CLI's output unmodified except for the single `**Model:**` line (step 8d) — never reshape or summarize it.
|
|
174
158
|
|
|
175
159
|
## Error reporting
|
|
176
160
|
|
|
@@ -101,7 +101,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
|
|
|
101
101
|
- Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
|
|
102
102
|
- Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
|
|
103
103
|
- For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage.
|
|
104
|
-
- When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/
|
|
104
|
+
- When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/validate_improvement_report.py`. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` — do NOT introduce new lens names or worker prefixes. `improvement-discovery` is NOT in the data.json schema enum, so author its markdown directly (not via `okstra-render-final-report.py`). Immediately after writing the markdown, run (`Bash`): `okstra inject-report-index <markdown path> --report-language <en|ko>`. That adds the top-of-report Index plus `I-NNN` / `C-NNN` scroll anchors; the run validator fails the report when the Index anchor is absent.
|
|
105
105
|
|
|
106
106
|
Write the data.json (and the audit sidecar `.md`) with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its only settings hook is the `SessionEnd` trace-cleanup; the coding-preflight hook emits reminders but never blocks). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. Then invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`. Confirm both files exist and respond with a short status line prefixed by your model identity, copied verbatim from the `**Model:** Report writer worker, <modelExecutionValue>` line in your dispatch prompt (per Worker Preamble → "Return message to the lead"):
|
|
107
107
|
|
|
@@ -112,15 +112,7 @@ data.json written to <abs path>; markdown rendered to <abs path>. Sections popul
|
|
|
112
112
|
|
|
113
113
|
## Error reporting
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
runs/<task-type>/worker-results/report-writer-worker-errors-<task-type>-<seq>.json
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Schema follows `team-contract` (Optional errors sidecar). Create the file with `{"schemaVersion": 1, "errors": []}` if missing, then append. Lead will dump it to the run error log after this subagent terminates.
|
|
122
|
-
|
|
123
|
-
There is NO `cli-failure` category for this worker — it has no external CLI to invoke. Treat MCP errors and Bash errors uniformly as `tool-failure`.
|
|
115
|
+
Record tool failures per the **Error reporting** contract in the preamble: extract the `**Errors sidecar path:**` from the dispatch prompt (return `REPORT_WRITER_ERRORS_PATH_MISSING` without proceeding if it is absent — do NOT synthesize a `runs/...` path), then append a `tool-failure` entry there for any failed tool call (Write of the prompt history file, mkdir, MCP call, Read of an input file), creating the file with `{"schemaVersion": 1, "errors": []}` if missing. The lead dumps the sidecar to the run error log after this subagent terminates. This worker has no external CLI, so there is no `cli-failure` category — treat MCP and Bash errors uniformly as `tool-failure`.
|
|
124
116
|
|
|
125
117
|
## Notes
|
|
126
118
|
|
|
@@ -38,7 +38,7 @@ PY
|
|
|
38
38
|
import os, sys
|
|
39
39
|
sys.path.insert(0, os.environ["OKSTRA_CTL_LIB_DIR"])
|
|
40
40
|
from pathlib import Path
|
|
41
|
-
from okstra_ctl import resolve_run_id, find_row_by_run_id, ResolveError
|
|
41
|
+
from okstra_ctl import resolve_run_id, find_row_by_run_id, ResolveError, resolve_under_root
|
|
42
42
|
home = Path(os.environ["OKSTRA_HOME_RESOLVED"])
|
|
43
43
|
try:
|
|
44
44
|
rid = resolve_run_id(home, os.environ["OK_QUERY"])
|
|
@@ -50,9 +50,12 @@ row = find_row_by_run_id(home, rid)
|
|
|
50
50
|
if row is None:
|
|
51
51
|
print(f"tail: row not found for {rid}", file=sys.stderr); sys.exit(2)
|
|
52
52
|
project_root = Path(row["projectRoot"])
|
|
53
|
+
run_dir = resolve_under_root(project_root, row.get("runDirRel"))
|
|
54
|
+
if run_dir is None:
|
|
55
|
+
print("tail: runDirRel escapes project root", file=sys.stderr); sys.exit(2)
|
|
53
56
|
status_rel = row.get("finalStatusRel") or ""
|
|
54
57
|
status_abs = str(project_root / status_rel) if status_rel else ""
|
|
55
|
-
print(
|
|
58
|
+
print(run_dir)
|
|
56
59
|
print(row.get("taskType", ""))
|
|
57
60
|
print(int(row.get("runSeq", 0)))
|
|
58
61
|
print(status_abs)
|
|
@@ -253,7 +253,12 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
253
253
|
while kill -0 "$agy_pid" 2>/dev/null; do
|
|
254
254
|
sleep "$poll_interval"
|
|
255
255
|
kill -0 "$agy_pid" 2>/dev/null || exit 0
|
|
256
|
-
|
|
256
|
+
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
257
|
+
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
258
|
+
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
259
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null)"
|
|
260
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null)"
|
|
261
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
257
262
|
now=$(date +%s)
|
|
258
263
|
idle=$(( now - last_mtime ))
|
|
259
264
|
if (( idle >= idle_timeout_secs )); then
|
|
@@ -99,7 +99,12 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
99
99
|
while kill -0 "$claude_pid" 2>/dev/null; do
|
|
100
100
|
sleep "$poll_interval"
|
|
101
101
|
kill -0 "$claude_pid" 2>/dev/null || exit 0
|
|
102
|
-
|
|
102
|
+
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
103
|
+
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
104
|
+
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
105
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null)"
|
|
106
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null)"
|
|
107
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
103
108
|
now=$(date +%s)
|
|
104
109
|
idle=$(( now - last_mtime ))
|
|
105
110
|
if (( idle >= idle_timeout_secs )); then
|
|
@@ -328,7 +328,12 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
328
328
|
while kill -0 "$codex_pid" 2>/dev/null; do
|
|
329
329
|
sleep "$poll_interval"
|
|
330
330
|
kill -0 "$codex_pid" 2>/dev/null || exit 0
|
|
331
|
-
|
|
331
|
+
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
332
|
+
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
333
|
+
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
334
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null)"
|
|
335
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null)"
|
|
336
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
332
337
|
now=$(date +%s)
|
|
333
338
|
idle=$(( now - last_mtime ))
|
|
334
339
|
if (( idle >= idle_timeout_secs )); then
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- every clarification row carries a recommended answer + one-line rationale inside the `Expected form` cell; a row that omits the recommendation is incomplete and must be completed before the report is finalised.
|
|
@@ -15,7 +15,7 @@ prompt (agents/workers/_cli-wrapper-template.md → Prompt Composition).
|
|
|
15
15
|
|
|
16
16
|
Load the applicable coding conventions for every language the diff will touch, then state in ONE line which conventions apply (e.g. `Applying TS + hexagonal overlay; domain at src/domains/*/domain/`). Lint/test green is necessary but NOT sufficient — self-mocked tests, interaction-only assertions, and untruthful names all pass a green pipeline; this gate is what keeps them out of the diff.
|
|
17
17
|
|
|
18
|
-
- **Resource selection — read the routed pack, never inline it here.** Use this worker prompt's `**Coding preflight pack:**` anchor header as the absolute path to the installed routed pack. Detect each touched file's language and framework
|
|
18
|
+
- **Resource selection — read the routed pack, never inline it here.** Use this worker prompt's `**Coding preflight pack:**` anchor header as the absolute path to the installed routed pack. Detect each touched file's language and framework from its extension or project manifest (`package.json`, `Cargo.toml`, `pyproject.toml`, `pom.xml`, `build.gradle*`, `prisma/schema.prisma`), then read that pack's resources via the Read tool by absolute path. Always read `overview.md` (the router) + `clean-code.md`, then select per the router's three ordered stages — Stage 1 language → `languages/<lang>.md`, Stage 2 framework → `frameworks/<fw>.md` (e.g. `frameworks/node-server.md` for server-side Node), Stage 3 architecture → `architectures/<arch>.md` (e.g. `architectures/hexagonal.md` for ports-and-adapters / NestJS-hex). Each stage is a list of rules; include EVERY matching resource (a change set can touch multiple languages/frameworks/architectures) — do not stop at the first match. These files are runtime resources, not Skill-tool skills, so always read them by path.
|
|
19
19
|
- **Project review rule packs:** also look for project-local review skills in `<PROJECT_ROOT>/skills/*review*`, `<PROJECT_ROOT>/.claude/skills/*review*`, and up to two parent directories' `skills/*review*/SKILL.md`. Read the relevant `SKILL.md` plus referenced `references/*.md` files and apply their rules during implementation. This is a prevention pass, not a PR-comment generation workflow: do not dispatch reviewer subagents from the executor. For Fonts Ninja-style PR review packs, the executor must avoid newly introduced duplicate helper stacks, tautological tests that merely re-call the delegated helper, self-mocking, domain rules in adapters/ports, domain objects outside `domain/`, dead APIs, weak public names, and functions that fail the plain-English read.
|
|
20
20
|
- **Language-agnostic principles that ALWAYS bind (the TDD loop MUST satisfy them):** (1) no self-mocking of the SUT — stub/spy only injected collaborators, never the subject's own methods; (2) behavioral assertions on outcomes (return value, state, persisted rows, events, boundary calls) — never `toHaveBeenCalled*` on an internal helper as the only/primary assertion; (3) truthful names — a `get*` / `find*` that writes/inserts, or a name encoding the caller's use-case (`*ForInit`) or hiding a domain rule (`findValid*`), is a defect; (4) single-purpose functions ≤50 effective lines, plain-English readability.
|
|
21
21
|
- **Graceful degradation (codex / antigravity executor runtimes, or any runtime where the resolved coding-preflight pack files are absent or unreadable):** do NOT skip the gate — apply the agnostic principles above plus the project's own `CLAUDE.md` / `CONTRIBUTING` / formatter+lint config, and record `coding-conventions: resource-unavailable → applied <project rules + agnostic principles>` in the final report. Never claim a resource read that did not happen.
|
|
@@ -16,7 +16,7 @@ profile document.
|
|
|
16
16
|
- **Phase 4 / 5 (independent analysis)**: analyser workers (`claude`, `codex`, `antigravity` when opted in) produce findings independently and have no access to one another's outputs. `report-writer` does not analyse.
|
|
17
17
|
- **Phase 5.5 (convergence — peer review by workers)**: the lead replays each analyser's findings to the *other* analysers and collects `AGREE` / `DISAGREE` / `SUPPLEMENT` verdicts across up to `effectiveMaxRounds` rounds. Workers act as peer reviewers of each other's findings in this phase; the lead mediates but does not vote. See `prompts/lead/convergence.md` for the round protocol, queue invariants, and final classification (`full-consensus` / `partial-consensus` / `contested` / `worker-unique`). For `requirements-discovery`, `error-analysis`, and `implementation-planning` this phase runs in **adversarial mode** (`convergence.adversarial=true`): verifiers try to refute each finding against its cited evidence and the burden of proof sits on the claim — see that skill's §"Adversarial Verification Mode".
|
|
18
18
|
- Do NOT conclude "no peer review happens" from the roster alone — every profile that lists ≥2 analyser workers runs convergence by default (`convergence.enabled=true` in `task-manifest.json`).
|
|
19
|
-
- **Pane-budget fallback (tolerance).** If a worker dispatch is rejected with `no room for another tmux split` (or an equivalent teammate-pane creation failure), the lead retries that worker in-process without `run_in_background
|
|
19
|
+
- **Pane-budget fallback (tolerance).** If a worker dispatch is rejected with `no room for another tmux split` (or an equivalent teammate-pane creation failure), the lead retries that worker in-process without `run_in_background`; if it was an external CLI worker, the lead instead **substitutes** an in-process `claude` analysis. Either way the lead records the substitution as provider unavailable in the run log and the convergence notes. Completed external-CLI worker trace panes are reclaimed automatically by the `SubagentStop` hook, but okstra cannot directly reclaim the teammate panes the harness creates, so this fallback is the last line of defence against pane-budget exhaustion. (This is a prompt instruction, not a code-enforced gate.)
|
|
20
20
|
- Tooling — read-only MCP availability (shared):
|
|
21
21
|
- MCP is not implicit okstra context. Query an MCP server only when the task brief explicitly lists it as source material for this run. Any MCP-derived finding MUST cite server, table, and the SELECT used. MCP MUST NEVER be used as a write path — schema/data mutations go through repository migration files reviewed by humans.
|
|
22
22
|
- Resource boundary (shared — artifact-home rule):
|
|
@@ -36,11 +36,11 @@ profile document.
|
|
|
36
36
|
- This recording is **silent internal setup**: the lead MUST emit NOTHING to the user about it — no pane id, no `%NNN`, no `lead-pane.id` path, no announcement of the phase-start-cleanup / wrap-up-disposition steps. Just record the file and proceed.
|
|
37
37
|
- Phase-start cleanup — prior-batch panes + completed teammates (shared — runs BEFORE dispatching each new worker batch, at the SAME boundaries: just before each `PROGRESS: phase-5.5-convergence round=<N>` marker and just before `PROGRESS: phase-6-synthesis dispatching report-writer-worker`):
|
|
38
38
|
- **(a) tmux panes.** okstra creates two kinds of tmux pane per run: **worker-agent panes** the harness gives to dispatched subagents (titled `claude-worker` / `codex-worker` / `antigravity-worker` / `report-writer-worker`), and **trace panes** the codex/antigravity wrappers spawn (`<cli>-<role>-<pid>-tail`). Both accumulate because each new batch spawns fresh panes and the prior ones are never reclaimed. The lead MUST ALWAYS run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<RUN_DIR>"` immediately before dispatching the next batch — NEVER gating this call on its own reading of `lead-pane.id`. The script self-resolves the lead pane and is a safe no-op outside tmux (it returns an empty pane list), so an always-run call cannot do harm; it closes every prior-batch okstra pane (worker-agent + trace) for this run while NEVER killing the lead's own pane. The lead MUST NOT fabricate a synthetic pane list.
|
|
39
|
-
- **(b) completed teammates (Teams mode only).** Each convergence round / critic / gap-verification / report-writer dispatch spawns teammates under fresh Agent `name`s (`<role>-reverify-r<N>`, `<provider>-worker-critic`, `report-writer-worker`, …); completed teammates do NOT leave the FleetView roster on their own — they linger as idle pills until run-end teardown, so a long run's roster grows unreadable. Before dispatching the next batch, the lead MUST send `SendMessage(to: <name>, message: { type: "shutdown_request" })`
|
|
39
|
+
- **(b) completed teammates (Teams mode only).** Each convergence round / critic / gap-verification / report-writer dispatch spawns teammates under fresh Agent `name`s (`<role>-reverify-r<N>`, `<provider>-worker-critic`, `report-writer-worker`, …); completed teammates do NOT leave the FleetView roster on their own — they linger as idle pills until run-end teardown, so a long run's roster grows unreadable. Before dispatching the next batch, the lead MUST send `SendMessage(to: <name>, message: { type: "shutdown_request" })` to every teammate it spawned earlier in THIS run **whose completion is already confirmed** (its Result Path exists / it is in the self-scheduled-polling done set). The `message` MUST be the object literal shown, NEVER a JSON string stuffed into a text field. This is the same graceful-shutdown primitive used at `Run-end teammate teardown`, applied per batch instead of once at the end.
|
|
40
40
|
- **Only confirmed-complete teammates, never the lead.** NEVER send a shutdown_request to a teammate whose result file has not yet appeared. In particular the critic runs CONCURRENTLY with the first reverify round (see `convergence` "Coverage critic pass"), so it MUST NOT be shut down until its own result is collected. NEVER target the lead's own session.
|
|
41
41
|
- **Do NOT call `TeamDelete` here.** The team stays alive for the remaining batches; only individual completed teammates are dismissed. `TeamDelete` remains a run-end-only step (`Run-end teammate teardown`).
|
|
42
42
|
- Silent-skip entirely in the no-`team_name` fallback (background subagents never join the roster) and in `tmux-pane` backend runs (external-lead workers are not session teammates).
|
|
43
|
-
- Both (a) and (b)
|
|
43
|
+
- Both (a) and (b) run **automatically without any interactive prompt**. Still emit one short, plain-language status line for the pair (e.g. `이전 배치 okstra pane 3개 · teammate 2명 정리`) and proceed — never expose internal identifiers (`%NNN`, `lead-pane.id`, raw Agent names, step names like "phase-start cleanup"). **Effect caveat:** Claude Code exposes no tool to surgically delete a roster entry mid-run, so a dismissed teammate may still show as an idle pill; the shutdown still reclaims its session and prevents the roster from growing with live members. On the FIRST batch of a run nothing has been dispatched yet — both steps no-op.
|
|
44
44
|
- Phase wrap-up — okstra pane disposition (shared, runs AFTER Phase 7 persistence/token collection and BEFORE teammate teardown):
|
|
45
45
|
- At run end the only residual okstra panes are the LAST phase's (e.g. the `report-writer-worker` agent pane and any codex/antigravity trace pane). `okstra-trace-cleanup.sh --list --run-dir "<RUN_DIR>"` returns one tab-separated `<pane_id>\t<pane_title>` line per residual okstra pane (worker-agent + trace) for this run.
|
|
46
46
|
- After the final-report file has been written and the routing recommendation has been issued, the lead MUST ALWAYS run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --list --run-dir "<RUN_DIR>"` exactly once — NEVER gating this call on its own reading of `lead-pane.id`. The output lists every residual okstra pane (worker-agent + trace) for this run, never the lead's own pane; outside tmux it is a safe no-op (empty output).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Shared Coverage-critic (opt-in) rule — single source for the byte-identical
|
|
3
|
+
bullet that used to be duplicated in requirements-discovery.md,
|
|
4
|
+
error-analysis.md, and implementation-planning.md. Those three phases run a
|
|
5
|
+
reused-worker Coverage critic pass and INCLUDE this file under their
|
|
6
|
+
"Cross-verification mode" block. final-verification.md uses a different
|
|
7
|
+
Acceptance critic and improvement-discovery.md has no critic, so neither
|
|
8
|
+
includes this file. Edit here once; the three phases pick the change up at
|
|
9
|
+
next render.
|
|
10
|
+
|
|
11
|
+
The bullet keeps its 2-space indent so it nests under "Cross-verification
|
|
12
|
+
mode:" when the include directive (placed at column 0) is resolved in-place.
|
|
13
|
+
|
|
14
|
+
Do NOT write the literal include directive token in this file's body — the
|
|
15
|
+
resolver matches it anywhere and would recurse on this file itself.
|
|
16
|
+
-->
|
|
17
|
+
- **Coverage critic (opt-in)**: when `convergence.critic.enabled=true` (chosen via the okstra-run picker or `--critic`), a reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface missed findings; its gaps are merged only after a 1-round adversarial reverify that follows convergence. See `prompts/lead/convergence.md` "Coverage critic pass".
|
|
@@ -14,7 +14,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
|
|
|
14
14
|
- **Commit list**: each commit's SHA (or short SHA), message, and the plan step it satisfies
|
|
15
15
|
- **Diff summary**: `git diff --stat <base>..HEAD` output, plus a per-file one-line summary of changes
|
|
16
16
|
- **Out-of-plan edits block**: every file edited that was not in the approved plan's file list, with rationale (empty block is acceptable and preferred)
|
|
17
|
-
- **Clarification answers carried in**: when `instruction-set/clarification-response.md` exists, it holds the user's answers to the approved plan's `## 1. Clarification Items` rows
|
|
17
|
+
- **Clarification answers carried in**: when `instruction-set/clarification-response.md` exists, it holds the user's answers to the approved plan's `## 1. Clarification Items` rows. The report MUST render the `## 0. Clarification Response Carried In From Previous Run` section (set `clarificationCarryIn.sourceFile` to that path) and, for every carried-over `## 1.` row whose answer appears there, record the answer in the `User input` cell and flip `Status` to `resolved`. A row left `open`/`answered` despite a matching user answer is a contract violation; an answer that materially changes scope beyond the approved plan is routed to a new `implementation-planning` run, not silently executed.
|
|
18
18
|
- **Stage sidecar evidence**: the JSON payload of `runs/<impl-task-key>/carry/stage-<N>.json` is embedded verbatim in a fenced ```json``` block, AND the `consumers.jsonl` rows this run appended are quoted line-by-line, so reviewers can audit the carry surface without grepping artifact directories.
|
|
19
19
|
- **Validation evidence**: actual command output (stdout/stderr) for every `pre / mid / post` validation command from the plan. Truncated output is acceptable but the command line and exit code MUST be exact. No paraphrasing of test results.
|
|
20
20
|
- **TDD evidence (when applicable)**: for steps that should be TDD-ordered, show the failing-test output BEFORE the implementation commit and the passing-test output AFTER, with commit SHAs framing the transition.
|
|
@@ -51,6 +51,6 @@ are collected and convergence finished. Phase 1-5 do not need it.
|
|
|
51
51
|
|
|
52
52
|
- Parse the executor's `### Stage Carry Evidence` JSON block. If absent or unparsable, end with status `contract-violated` and route to a follow-up `error-analysis`.
|
|
53
53
|
- For this run's single stage: write its JSON verbatim to `runs/<impl-task-key>/carry/stage-<N>.json`. Refuse to overwrite an existing file (one stage = one sidecar; re-runs are out of scope for this version).
|
|
54
|
-
- For this run's single stage: append a `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` with `completed_at`, `carry_path`, `report_path` (this run's final-report path relative to the run root), and the SHA of HEAD. Append it with `okstra_ctl.consumers.append_consumer` (NOT a raw filesystem write) —
|
|
54
|
+
- For this run's single stage: append a `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` with `completed_at`, `carry_path`, `report_path` (this run's final-report path relative to the run root), and the SHA of HEAD. Append it with `okstra_ctl.consumers.append_consumer` (NOT a raw filesystem write) — that call honours the consumers lock AND releases this stage's worktree-registry occupancy, so later runs stop seeing a finished stage as a concurrent run. `report_path` lets `final-verification` cite each stage's originating report when assembling its Source Implementation Report list.
|
|
55
55
|
- The verifier round, Phase 5.5 convergence, and this Phase 6 report run **once per run** over this stage's diff — NOT per step.
|
|
56
56
|
- Quote this stage's new contents (the sidecar JSON in full and the new consumers row by itself) in the final report's `Stage sidecar evidence` deliverable section.
|
|
@@ -13,13 +13,13 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
|
|
|
13
13
|
|
|
14
14
|
- **Executor dispatch labelling.** When Lead dispatches the Executor it MUST set the Agent `name` to `<provider>-executor` (e.g. `codex-executor`, `claude-executor`) — NOT the generic `<provider>-worker` — so the FleetView teammate pill names the job; see `prompts/lead/okstra-lead-contract.md` Phase 4 "Agent `name` on dispatch". For a `codex` / `antigravity` Executor, Lead MUST additionally inject a `**Pane role:** executor` line into the dispatched prompt body so the CLI wrapper titles its tmux trace pane `<cli>-executor-<pid>` (the wrapper reads that line per `agents/workers/_cli-wrapper-template.md`). Token attribution is unaffected — the collector matches any `agentName` beginning `<provider>-`.
|
|
15
15
|
- The `Executor` (bound in `implementation.md` thin core) is the **only worker permitted to use Edit / Write / state-mutating Bash commands** on project files. All other workers run read-only. When the executor provider is `codex` or `antigravity`, the actual file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --sandbox workspace-write`, antigravity's equivalent) — not through Claude-side Edit/Write tools — but the safety rules in this sidecar still apply identically.
|
|
16
|
-
-
|
|
16
|
+
- When the thin core's Task worktree block resolves status to `created` or `reused`, the Executor MUST run every Edit / Write / build / test / commit command with the worktree path as cwd. Treat it as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. Do NOT `cd` out of the worktree to reach files. If a file outside the worktree is genuinely needed, treat it as a planning gap: record it in `Out-of-plan edits` and continue.
|
|
17
17
|
- **How to set cwd per Bash call**: the Claude Bash tool inherits its cwd from the lead session, which is NOT the worktree. To put cwd-sensitive toolchains (`cargo`, `npm`, `pnpm`, `bun`, `pytest`, `make`, `go`) into the worktree, prefix the command with `cd {{EXECUTOR_WORKTREE_PATH}} && ` inside the same Bash invocation — e.g. `cd {{EXECUTOR_WORKTREE_PATH}} && cargo test -p foo`. **Never wrap in `bash -lc "..."` or `bash -c "..."`** — the wrapper hides the leading `cd` token from Claude Code's permission auto-allow layer (causing prompts on every call) without any safety benefit. For tools that accept an explicit working-directory flag (`git -C <path>`, `cargo --manifest-path`, `pytest --rootdir`), prefer that form over the `cd && ` chain. Edit / Write / Read tool calls already use absolute paths and need no cwd handling. The codex / antigravity executor CLI wrappers (`okstra-codex-exec.sh -C`, `okstra-antigravity-exec.sh --include-directories`) already inject worktree cwd at the CLI layer, so this rule applies primarily to the Claude executor.
|
|
18
18
|
- **Synced okstra state directory.** At provision time `okstra-ctl` may symlink `.project-docs/` from the repo's **main worktree** into the task worktree. This is NOT an independent copy — writes through it land in the main worktree. Inside this run the executor MUST confine okstra artifact writes to its own task scope (i.e. `.okstra/tasks/<this-task-id>/...`). Other synced directories, if present due to local configuration, are not implicit okstra context; read them only when the brief explicitly cites them as source material.
|
|
19
19
|
|
|
20
20
|
## Pre-implementation context exploration (executor before first edit)
|
|
21
21
|
|
|
22
|
-
- **Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop below).** The gate body is a single source at `prompts/profiles/_coding-conventions-preflight.md` (sibling of this sidecar)
|
|
22
|
+
- **Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop below).** The gate body is a single source at `prompts/profiles/_coding-conventions-preflight.md` (sibling of this sidecar). Do NOT re-type that content from memory — deliver it by file so it cannot drift or be dropped:
|
|
23
23
|
- **Claude executor:** Read `_coding-conventions-preflight.md` end-to-end before the first `Edit` / `Write`, then state in ONE line which conventions apply (e.g. `Applying TS + hexagonal overlay; domain at src/domains/*/domain/`).
|
|
24
24
|
- **CLI executor (BLOCKING when the executor provider is `codex` or `antigravity`):** the executor CLI process does NOT share the lead's context, and it cannot Read this sidecar's directory — that path sits outside the CLI sandbox and the CLI only sees its stdin prompt, so a file reference never reaches it. The lead MUST physically append the **body** of `_coding-conventions-preflight.md` into the persisted executor prompt at dispatch time: Read the file from the same absolute directory you read this sidecar from, then `Write`/`cat` its body into the persisted prompt. Never hand-retype it. Enforcement: the CLI wrapper agents refuse an implementation-Executor dispatch whose persisted prompt lacks the literal heading `Coding-conventions preflight`, returning `<SENTINEL_PREFIX>_PREFLIGHT_MISSING` (see `agents/workers/_cli-wrapper-template.md` → Prompt Composition).
|
|
25
25
|
- **Completion self-check (BLOCKING — runs BEFORE you claim the stage done).** The gate body is a single source at `prompts/profiles/_implementation-self-check.md` (sibling of this sidecar): the four-item completion gate (functions ≤50 lines, conventions applied, truthful names & why-comments, real build/test run). Do NOT re-type it from memory — deliver it by file so it cannot drift.
|
|
@@ -16,4 +16,5 @@ done on a failing item.
|
|
|
16
16
|
- functions: every new/edited function ≤50 effective lines, single purpose
|
|
17
17
|
- conventions: applied the routed pack + project patterns (name which ones)
|
|
18
18
|
- names & comments: names say what, comments say why; no obvious-restatement comments; no truthful-name violations
|
|
19
|
-
- verification: ran the actual build/test (paste the command + its result)
|
|
19
|
+
- verification: ran the actual build/test (paste the command + its result)
|
|
20
|
+
- cleanup: no dead or commented-out code left behind
|