okstra 0.102.2 → 0.102.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +5 -5
- package/README.md +5 -5
- package/docs/kr/architecture/storage-model.md +8 -3
- package/docs/kr/architecture.md +12 -13
- package/docs/kr/cli.md +4 -4
- package/docs/kr/container.md +1 -1
- package/docs/kr/performance-improvement-plan-v2.md +2 -2
- package/docs/project-structure-overview.md +2 -2
- package/package.json +7 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/lib/okstra/interactive.sh +12 -0
- package/runtime/bin/okstra-antigravity-exec.sh +1 -2
- package/runtime/bin/okstra-codex-exec.sh +1 -2
- package/runtime/bin/okstra-render-final-report.py +2 -9
- package/runtime/bin/okstra-spawn-followups.py +4 -6
- package/runtime/bin/okstra-trace-cleanup.sh +24 -4
- package/runtime/prompts/lead/okstra-lead-contract.md +2 -2
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +9 -11
- package/runtime/python/okstra_ctl/codex_dispatch.py +6 -13
- package/runtime/python/okstra_ctl/consumers.py +35 -0
- package/runtime/python/okstra_ctl/container.py +4 -6
- package/runtime/python/okstra_ctl/dispatch_core.py +8 -13
- package/runtime/python/okstra_ctl/final_report_paths.py +24 -0
- package/runtime/python/okstra_ctl/handoff.py +2 -1
- package/runtime/python/okstra_ctl/implementation_outcome.py +271 -0
- package/runtime/python/okstra_ctl/implementation_stage.py +7 -11
- package/runtime/python/okstra_ctl/path_hints.py +665 -0
- package/runtime/python/okstra_ctl/render.py +4 -3
- package/runtime/python/okstra_ctl/report_view_artifacts.py +14 -0
- package/runtime/python/okstra_ctl/report_views.py +7 -4
- package/runtime/python/okstra_ctl/run.py +3 -10
- package/runtime/python/okstra_ctl/run_context.py +4 -2
- package/runtime/python/okstra_ctl/stage_targets.py +18 -0
- package/runtime/python/okstra_ctl/wizard.py +37 -26
- package/runtime/python/okstra_ctl/wizard_stage_intent.py +54 -0
- package/runtime/python/okstra_project/state.py +40 -2
- package/runtime/python/okstra_token_usage/report.py +2 -5
- package/runtime/validators/validate-report-views.py +2 -1
- package/runtime/validators/validate-run.py +1 -11
- package/src/commands/lifecycle/install.mjs +5 -5
- package/src/commands/lifecycle/uninstall.mjs +4 -4
- package/docs/superpowers/plans/2026-05-12-ticket-id-in-reports.md +0 -638
- package/docs/superpowers/plans/2026-05-14-convergence-queue-pruning.md +0 -1568
- package/docs/superpowers/plans/2026-05-17-dual-format-final-report.md +0 -167
- package/docs/superpowers/plans/2026-05-20-final-report-language.md +0 -1501
- package/docs/superpowers/plans/2026-05-20-implementation-planning-multi-stage.md +0 -1267
- package/docs/superpowers/plans/2026-05-20-okstra-run-prompt-sot-b1.md +0 -1007
- package/docs/superpowers/plans/2026-05-20-wizard-messages-json-sot.md +0 -720
- package/docs/superpowers/plans/2026-05-20-wizard-prompt-json-sot-a1.md +0 -681
- package/docs/superpowers/plans/2026-05-21-improvement-discovery-task-type.md +0 -1691
- package/docs/superpowers/plans/2026-05-24-implementation-lead-context-slimming.md +0 -1700
- package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +0 -158
- package/docs/superpowers/plans/2026-05-26-wizard-3-option-picker.md +0 -860
- package/docs/superpowers/plans/2026-06-02-final-verification-protocol-hardening.md +0 -326
- package/docs/superpowers/plans/2026-06-02-okstra-run-branch-confirm-step.md +0 -337
- package/docs/superpowers/plans/2026-06-02-okstra-run-phase-pane-cleanup.md +0 -410
- package/docs/superpowers/plans/2026-06-02-requirements-discovery-fanout.md +0 -728
- package/docs/superpowers/plans/2026-06-04-adversarial-implementation-planning.md +0 -294
- package/docs/superpowers/plans/2026-06-04-adversarial-verification.md +0 -570
- package/docs/superpowers/plans/2026-06-04-coverage-critic.md +0 -516
- package/docs/superpowers/plans/2026-06-04-stage-cohesion-planner.md +0 -351
- package/docs/superpowers/plans/2026-06-04-stage-run-batching.md +0 -457
- package/docs/superpowers/plans/2026-06-05-acceptance-critic.md +0 -251
- package/docs/superpowers/plans/2026-06-05-compact-markdown-report-tables.md +0 -323
- package/docs/superpowers/plans/2026-06-05-wizard-batch-prompts.md +0 -559
- package/docs/superpowers/plans/2026-06-06-final-verification-whole-task-gate.md +0 -993
- package/docs/superpowers/plans/2026-06-06-stage-parallel-and-pending-fixes.md +0 -93
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p1.md +0 -447
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p2.md +0 -289
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p3.md +0 -774
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p4.md +0 -303
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p5-multidep-base.md +0 -387
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md +0 -275
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase3.md +0 -282
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4a.md +0 -147
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4b.md +0 -262
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4c.md +0 -184
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4d.md +0 -88
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4e.md +0 -250
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa.md +0 -409
- package/docs/superpowers/plans/2026-06-09-implementation-run-artifact-stage-isolation.md +0 -320
- package/docs/superpowers/plans/2026-06-10-concurrent-run-team-guard.md +0 -456
- package/docs/superpowers/plans/2026-06-10-git-reconcile-stale-sha-recovery.md +0 -1408
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling-PROBE.md +0 -42
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling.md +0 -337
- package/docs/superpowers/plans/2026-06-10-p6-token-usage-incremental.md +0 -1029
- package/docs/superpowers/plans/2026-06-10-stage-group-handoff.md +0 -1572
- package/docs/superpowers/plans/2026-06-11-fix-cycle.md +0 -1290
- package/docs/superpowers/plans/2026-06-11-wizard-whole-task-final-verification.md +0 -526
- package/docs/superpowers/plans/2026-06-12-html-plan-approval.md +0 -1000
- package/docs/superpowers/plans/2026-06-13-repo-risk-hardening.md +0 -493
- package/docs/superpowers/plans/2026-06-14-host-runtime-auto-execution.md +0 -1775
- package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +0 -504
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +0 -342
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +0 -258
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +0 -387
- package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +0 -749
- package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +0 -828
- package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +0 -490
- package/docs/superpowers/plans/2026-06-17-okstra-error-report.md +0 -724
- package/docs/superpowers/plans/2026-06-18-subagent-pane-reclaim.md +0 -483
- package/docs/superpowers/plans/2026-06-19-agent-operating-standard.md +0 -476
- package/docs/superpowers/plans/2026-06-19-okstra-inspect-recap-facet.md +0 -647
- package/docs/superpowers/plans/2026-06-20-error-feedback-loop.md +0 -1019
- package/docs/superpowers/plans/2026-06-20-run-index-row-normalization.md +0 -583
- package/docs/superpowers/plans/2026-06-20-stage-auto-integrate-teardown.md +0 -819
- package/docs/superpowers/plans/2026-06-21-okstra-container-local-user-test.md +0 -714
- package/docs/superpowers/plans/2026-06-21-release-handoff-local-checkout.md +0 -348
- package/docs/superpowers/plans/2026-06-23-implementation-stage-multiselect-chain.md +0 -630
- package/docs/superpowers/plans/2026-06-23-implementation-step-commit-granularity.md +0 -274
- package/docs/superpowers/plans/2026-06-23-phase-batch-cleanup-enforcement.md +0 -409
- package/docs/superpowers/plans/2026-06-24-cross-project-precondition.md +0 -542
- package/docs/superpowers/plans/2026-06-24-decision-drafts-render-gap.md +0 -568
- package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +0 -203
- package/docs/superpowers/plans/2026-06-24-okstra-container-build-rename.md +0 -525
- package/docs/superpowers/specs/2026-05-12-ticket-id-in-reports-design.md +0 -131
- package/docs/superpowers/specs/2026-05-15-implementation-plan-verification-design.md +0 -254
- package/docs/superpowers/specs/2026-05-20-final-report-language-design.md +0 -383
- package/docs/superpowers/specs/2026-05-20-implementation-planning-multi-stage-design.md +0 -322
- package/docs/superpowers/specs/2026-05-20-okstra-run-prompt-sot-design.md +0 -299
- package/docs/superpowers/specs/2026-05-21-improvement-discovery-task-type-design.md +0 -335
- package/docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md +0 -113
- package/docs/superpowers/specs/2026-06-02-okstra-run-phase-pane-cleanup-design.md +0 -173
- package/docs/superpowers/specs/2026-06-02-requirements-discovery-fanout-design.md +0 -154
- package/docs/superpowers/specs/2026-06-04-adversarial-implementation-planning-design.md +0 -90
- package/docs/superpowers/specs/2026-06-04-adversarial-verification-design.md +0 -176
- package/docs/superpowers/specs/2026-06-04-coverage-critic-design.md +0 -99
- package/docs/superpowers/specs/2026-06-04-stage-splitting-cost-aware-design.md +0 -98
- package/docs/superpowers/specs/2026-06-05-acceptance-critic-design.md +0 -90
- package/docs/superpowers/specs/2026-06-05-compact-markdown-report-tables-design.md +0 -87
- package/docs/superpowers/specs/2026-06-05-wizard-batch-prompts-design.md +0 -121
- package/docs/superpowers/specs/2026-06-06-final-verification-whole-task-gate-design.md +0 -126
- package/docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md +0 -180
- package/docs/superpowers/specs/2026-06-06-vertical-slice-tdd-planning-design.md +0 -179
- package/docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md +0 -169
- package/docs/superpowers/specs/2026-06-09-executor-model-custom-id-cascade-design.md +0 -66
- package/docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md +0 -87
- package/docs/superpowers/specs/2026-06-10-blocking-contract-posthoc-conformance-design.md +0 -168
- package/docs/superpowers/specs/2026-06-10-concurrent-run-team-guard-design.md +0 -107
- package/docs/superpowers/specs/2026-06-10-git-reconcile-stale-sha-recovery-design.md +0 -105
- package/docs/superpowers/specs/2026-06-10-lead-worker-completion-polling-design.md +0 -113
- package/docs/superpowers/specs/2026-06-10-stage-group-handoff-design.md +0 -156
- package/docs/superpowers/specs/2026-06-11-brief-entry-only-handoff-stage-entry-design.md +0 -158
- package/docs/superpowers/specs/2026-06-11-fix-cycle-design.md +0 -94
- package/docs/superpowers/specs/2026-06-11-wizard-whole-task-final-verification-design.md +0 -89
- package/docs/superpowers/specs/2026-06-12-codex-lead-adapter-design.md +0 -358
- package/docs/superpowers/specs/2026-06-12-html-plan-approval-design.md +0 -85
- package/docs/superpowers/specs/2026-06-12-stage-discipline-rule-design.md +0 -108
- package/docs/superpowers/specs/2026-06-13-forbidden-actions-ssot-design.md +0 -134
- package/docs/superpowers/specs/2026-06-13-neutral-tmux-lead-adapter-design.md +0 -284
- package/docs/superpowers/specs/2026-06-14-host-runtime-auto-execution-design.md +0 -404
- package/docs/superpowers/specs/2026-06-17-okstra-error-report-design.md +0 -123
- package/docs/superpowers/specs/2026-06-18-subagent-pane-reclaim-design.md +0 -150
- package/docs/superpowers/specs/2026-06-19-agent-operating-standard-design.md +0 -120
- package/docs/superpowers/specs/2026-06-19-okstra-inspect-recap-facet-design.md +0 -96
- package/docs/superpowers/specs/2026-06-20-error-feedback-loop-design.md +0 -171
- package/docs/superpowers/specs/2026-06-20-run-index-row-normalization-design.md +0 -112
- package/docs/superpowers/specs/2026-06-20-stage-auto-integrate-teardown-design.md +0 -145
- package/docs/superpowers/specs/2026-06-21-okstra-container-local-user-test-design.md +0 -125
- package/docs/superpowers/specs/2026-06-21-release-handoff-local-checkout-design.md +0 -113
- package/docs/superpowers/specs/2026-06-23-implementation-stage-multiselect-chain-design.md +0 -138
- package/docs/superpowers/specs/2026-06-23-implementation-step-commit-granularity-design.md +0 -120
- package/docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md +0 -129
- package/docs/superpowers/specs/2026-06-24-decision-drafts-render-gap-design.md +0 -185
- package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +0 -121
- package/docs/superpowers/specs/2026-06-24-okstra-container-build-rename-design.md +0 -110
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# run-index row 정규화 (runId SSOT화)
|
|
2
|
-
|
|
3
|
-
- 상태: 설계 승인 대기
|
|
4
|
-
- 작성일: 2026-06-20
|
|
5
|
-
- 범위: `~/.okstra/{recent,active}.jsonl` + `~/.okstra/projects/<id>/index.jsonl` 의 run-index row 스키마
|
|
6
|
-
|
|
7
|
-
## 1. 배경 / 문제
|
|
8
|
-
|
|
9
|
-
run-index row 는 현재 18개 필드를 가지며, 그 중 다수가 `runId` 또는 다른 평면 필드에서 파생 가능한 **중복 값**이다.
|
|
10
|
-
한 레코드는 `record_start()` 가 [`scripts/okstra_ctl/index.py:74`](../../../scripts/okstra_ctl/index.py:74) 에서 조립하고
|
|
11
|
-
`recent.jsonl`·`active.jsonl`·`projects/<id>/index.jsonl` 세 곳에 동일하게 기록된다.
|
|
12
|
-
|
|
13
|
-
중복은 두 층위로 나뉜다.
|
|
14
|
-
|
|
15
|
-
- **① 파일 간 row 복제** — 같은 row 가 recent / index / active 에 복사된다. 이는 reader 가 join 없이 한 파일만 읽도록 한 **의도된 비정규화 인덱스**이며 이번 범위에서 **건드리지 않는다**.
|
|
16
|
-
- **② 레코드 내 파생 필드** — 한 row 안에서 다른 필드로부터 계산 가능한 값들. **이번 작업의 대상**이다.
|
|
17
|
-
|
|
18
|
-
`runId` 형식은 [`scripts/okstra_ctl/ids.py:8`](../../../scripts/okstra_ctl/ids.py:8) 에 정의된 `<projectId>/<taskGroup>/<taskId>/<taskType>/r<runSeq>` 슬러그이고,
|
|
19
|
-
역파서 `parse_run_id()` 가 [`scripts/okstra_ctl/ids.py:34`](../../../scripts/okstra_ctl/ids.py:34) 에 **이미 존재**한다.
|
|
20
|
-
|
|
21
|
-
## 2. 검증 (설계의 근거)
|
|
22
|
-
|
|
23
|
-
기존 246개 row(recent 124 + 6개 프로젝트 index + active) 전체에 대해 "평면 필드 == `parse_run_id(runId)` 결과" 및 "경로 필드 == 규칙 재구성" 일치 여부를 전수 검사했다.
|
|
24
|
-
|
|
25
|
-
| 필드 | 결과 | 판정 |
|
|
26
|
-
|---|---|---|
|
|
27
|
-
| `projectId` | 불일치 0 | 무손실 파생 가능 |
|
|
28
|
-
| `taskType` | 불일치 0 | 무손실 파생 가능 |
|
|
29
|
-
| `runSeq` | 불일치 0 | 무손실 파생 가능 |
|
|
30
|
-
| `invocationFile` | 불일치 0 | 규칙 재구성 가능 (slug 기반) |
|
|
31
|
-
| `taskGroup` | 불일치 74 | **파생 불가** — 평면값은 raw 대소문자(`DEV-9388`), runId 세그먼트는 slug(`dev-9388`) |
|
|
32
|
-
| `taskId` | 불일치 48 | **파생 불가** — 동일 사유 |
|
|
33
|
-
| `taskKey` | 불일치 74 | runId 파싱으로는 불가, 단 평면 `taskGroup`+`taskId` 조합으로는 무손실 |
|
|
34
|
-
| `runDirRel` | 불일치 148 | **파생 불가** — 옛 데이터는 `.project-docs/okstra/tasks/...` prefix(artifact-home 변경 이력), 카운터 분리로 규칙 재구성 불가 |
|
|
35
|
-
|
|
36
|
-
**핵심 교훈**: `taskGroup`/`taskId`/`taskKey` 는 raw 대소문자를 보존하므로 runId(slug)에서 복원하면 정보가 손실된다. 따라서 raw 평면값을 유지해야 한다.
|
|
37
|
-
|
|
38
|
-
## 3. 결정
|
|
39
|
-
|
|
40
|
-
### 3.1 제거 — 5개 필드 (검증상 무손실 복원 가능)
|
|
41
|
-
|
|
42
|
-
| 제거 필드 | 복원 방법 |
|
|
43
|
-
|---|---|
|
|
44
|
-
| `projectId` | `parse_run_id(runId)["projectId"]` |
|
|
45
|
-
| `taskType` | `parse_run_id(runId)["taskType"]` |
|
|
46
|
-
| `runSeq` | `parse_run_id(runId)["runSeq"]` |
|
|
47
|
-
| `taskKey` | `f"{row['taskGroup']}/{row['taskId']}"` (평면 raw 조합) |
|
|
48
|
-
| `invocationFile` | `projects/<pid>/invocations/<slug taskGroup>/<slug taskId>/<taskType>/r<NN>.json` — `parse_run_id` 의 slug 세그먼트 사용 |
|
|
49
|
-
|
|
50
|
-
### 3.2 유지 — 검증이 "제거 불가"로 판정
|
|
51
|
-
|
|
52
|
-
`taskGroup`, `taskId` (raw 케이스), `runDirRel`, `finalReportRel`, `finalStatusRel` (실저장값),
|
|
53
|
-
`projectRoot` (meta.json 에 SSOT 가 있으나 **과거 run 당시 경로** 보존 + meta 조회 비용 회피 위해 유지),
|
|
54
|
-
가변 필드 `status`/`startedAt`/`finishedAt`/`workers`/`leadModel`/`validation`.
|
|
55
|
-
|
|
56
|
-
결과: **18필드 → 13필드.** 가장 큰 절감은 긴 경로인 `invocationFile`.
|
|
57
|
-
|
|
58
|
-
남는 slim row 필드:
|
|
59
|
-
`runId`, `projectRoot`, `taskGroup`, `taskId`, `status`, `startedAt`, `finishedAt`, `workers`, `leadModel`, `validation`, `finalReportRel`, `finalStatusRel`, `runDirRel`.
|
|
60
|
-
|
|
61
|
-
## 4. 구조
|
|
62
|
-
|
|
63
|
-
### 4.1 단일 참조점 두 개
|
|
64
|
-
|
|
65
|
-
복원/생성 로직을 두 개의 헬퍼에 집중한다. 신규 모듈 `scripts/okstra_ctl/run_index_row.py`.
|
|
66
|
-
|
|
67
|
-
- **`hydrate_run_row(row) -> dict`** — slim row(또는 기존 full row)를 받아 제거된 5필드를 채운 full dict 반환. full row 를 받으면 기존 값을 덮어쓰지 않고 동일 결과를 보장(멱등). reader 전용.
|
|
68
|
-
- **`build_run_index_row(...) -> dict`** — slim row 를 생성하는 유일한 진입점. 현재 `index.py` 의 row dict 리터럴 두 곳([`record_start`](../../../scripts/okstra_ctl/index.py:74), [`reserve_run_in_active`](../../../scripts/okstra_ctl/index.py:162))과 reconcile/backfill 의 row 생성이 이 헬퍼를 호출하도록 치환. writer 전용.
|
|
69
|
-
|
|
70
|
-
### 4.2 read 지점 hydrate (reader 로직 무변경)
|
|
71
|
-
|
|
72
|
-
디스크는 slim, 메모리는 full 을 유지한다. run-index 전용 read 함수 `read_run_index(path) -> list[dict]` 를 도입해 각 row 를 `hydrate_run_row` 로 통과시킨다.
|
|
73
|
-
|
|
74
|
-
run-index row 소비자 6곳의 **read 진입점만** 교체하고, 그 아래 소비 로직은 그대로 둔다.
|
|
75
|
-
|
|
76
|
-
- [`scripts/okstra_ctl/sequence.py`](../../../scripts/okstra_ctl/sequence.py) — `projectId`/`taskType`/`runSeq` 로 next seq 계산
|
|
77
|
-
- [`scripts/okstra_ctl/listing.py`](../../../scripts/okstra_ctl/listing.py) — `projectId`/`taskType`/`invocationFile`/`projectRoot`
|
|
78
|
-
- [`scripts/okstra_ctl/reconcile.py`](../../../scripts/okstra_ctl/reconcile.py) — `projectId`/`taskType`/`runSeq`/`runDirRel`
|
|
79
|
-
- [`scripts/okstra_ctl/pane_reclaim.py`](../../../scripts/okstra_ctl/pane_reclaim.py) — `projectRoot`
|
|
80
|
-
- [`scripts/okstra_ctl/wizard.py`](../../../scripts/okstra_ctl/wizard.py) — `taskKey`/`taskType`
|
|
81
|
-
- [`scripts/okstra_ctl/recap.py`](../../../scripts/okstra_ctl/recap.py) — `taskType`
|
|
82
|
-
|
|
83
|
-
`reconcile.py` 는 row 를 읽어 다시 쓰는데(in-place 갱신), 쓸 때는 `build_run_index_row` 로 slim 화한다 → 자연스러운 lazy 재작성 경로가 된다.
|
|
84
|
-
|
|
85
|
-
> manifest / state / convergence 를 읽는 코드(`context_cost.py`, `render.py`, `handoff.py`, `codex_dispatch.py`, `state.py`, `error_report.py`, `task-show.mjs` 등)는 run-index row 소비자가 **아니므로** 변경 대상이 아니다.
|
|
86
|
-
|
|
87
|
-
## 5. 마이그레이션 (일괄)
|
|
88
|
-
|
|
89
|
-
`migrate.py` 는 `.project-docs/okstra/ → .okstra/` **경로 이동** 전용 one-shot 이고 project_root 레벨이라, row 스키마 변환과 책임이 다르다. **혼합하지 않는다.**
|
|
90
|
-
|
|
91
|
-
별도 일괄 변환을 둔다 — `run_index_row.py` 의 `reindex_slim(home) -> int` 함수. 동작:
|
|
92
|
-
|
|
93
|
-
1. `recent.jsonl`, `active.jsonl`, 모든 `projects/<id>/index.jsonl` 을 읽는다.
|
|
94
|
-
2. 각 row 를 `slim_run_row` 로 정규화해 slim 으로 재작성한다(원자적 tmp + os.replace).
|
|
95
|
-
3. 이미 slim 인 row 는 no-op (멱등). `runId` 없는 비-run-index row(옛 event-log)는 무변형 통과.
|
|
96
|
-
4. `archive/` 는 현재 비어 있어 대상 외. 미래 archive 는 writer 가 이미 slim 을 기록하므로 자동 정합.
|
|
97
|
-
|
|
98
|
-
이 함수는 기존 데이터 1회 정리용 **일회성 마이그레이션**이라 CLI 서브커맨드로 노출하지 않는다(YAGNI). writer 가 항상 slim 을 기록하므로 새 환경은 처음부터 slim 이고, `hydrate_run_row` 가 full row 도 수용(안전망)해 미마이그레이션 환경도 reader 가 정상 동작한다. pre-v1.0 이라 하위호환 부담도 없다.
|
|
99
|
-
|
|
100
|
-
## 6. 테스트 (`tests/run/`)
|
|
101
|
-
|
|
102
|
-
- `parse_run_id` ↔ `build_run_id` 라운드트립 (slug 세그먼트 기준).
|
|
103
|
-
- `hydrate_run_row(slim)` == 기존 full row — 실데이터 기반 회귀 스냅샷(`projectId`/`taskType`/`runSeq`/`taskKey`/`invocationFile` 5필드가 원본과 일치).
|
|
104
|
-
- `hydrate_run_row(full)` 멱등 — full row 를 넣어도 동일 결과.
|
|
105
|
-
- `reindex_slim` 멱등 — 두 번 실행해도 동일 출력.
|
|
106
|
-
- raw 케이스 보존 — `taskGroup="DEV-9388"` 인 row 가 마이그레이션 후에도 raw 유지되는지.
|
|
107
|
-
|
|
108
|
-
## 7. 비범위 (YAGNI)
|
|
109
|
-
|
|
110
|
-
- 파일 간 row 복제(① recent/index/active 통합) — 조회 경로 전면 개편이 필요하고 이번 동기와 무관.
|
|
111
|
-
- `projectRoot`/`runDirRel`/`finalReportRel`/`finalStatusRel` 제거 — 검증상 파생 불가 또는 실익 모호.
|
|
112
|
-
- `runId` 포맷 변경 — fs-safe 슬러그 불변식(충돌 방지)을 유지해야 하므로 불가.
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# stage 자동 통합(머지) + worktree teardown — 설계
|
|
2
|
-
|
|
3
|
-
- 작성일: 2026-06-20
|
|
4
|
-
- 범위: `implementation` 으로 쌓인 stage 들을 task 브랜치(부모)에 **okstra 가 자동 머지**하고, 역할이 끝난 stage worktree·registry 키·stage 브랜치를 **자동 정리(teardown)** 한다. whole-task final-verification 진입 시 자동 수행하고, 동일 코어를 단독 명령으로도 노출한다.
|
|
5
|
-
- 비범위
|
|
6
|
-
- **stage-group handoff 경로 변경 없음** — release-handoff 의 stage-group(`assemble`) 모드는 그대로 둔다. 본 설계는 whole-task 통합 경로 전용이며, 두 워크플로우는 상호배타다(§2.4).
|
|
7
|
-
- **충돌 자동 해소 없음** — stage 간 머지 충돌은 `git merge --abort` 후 중단하고 사용자 수동 해소로 넘긴다(octopus/전략 머지 없음).
|
|
8
|
-
- **dirty worktree 강제 삭제 없음** — 미커밋 변경이 남은 stage worktree 는 teardown 하지 않고 경고만 남긴다.
|
|
9
|
-
- `implementation` 외 phase 의 worktree 모델 불변 — 기존 task-key worktree 1개 유지.
|
|
10
|
-
- 관계: [`2026-06-06-stage-worktree-isolation-design.md`](2026-06-06-stage-worktree-isolation-design.md) 의 stage worktree 격리 모델 위에 선다. 그 spec 의 **§2 "okstra 자동 머지 없음" 원칙을 본 설계가 의도적으로 전환**한다(§2.1). 그 spec 이 후속 과제로 남긴 **§9-#2 "stage worktree 자동 teardown"** 를 본 설계가 해소한다. [`2026-06-10-stage-group-handoff-design.md`](2026-06-10-stage-group-handoff-design.md) 의 stage-group 수집 머지와는 진입 경로가 분리되어 충돌하지 않는다(§2.4).
|
|
11
|
-
|
|
12
|
-
## 1. 동기
|
|
13
|
-
|
|
14
|
-
whole-task final-verification 게이트([`stage_targets.resolve_whole_task_target`](../../../scripts/okstra_ctl/stage_targets.py:196))는 두 가지를 강제한다.
|
|
15
|
-
|
|
16
|
-
1. **merged 게이트** — 모든 stage 의 done commit 이 task worktree HEAD 의 ancestor여야 함. 아니면 "merge stage branches then retry" 로 중단.
|
|
17
|
-
2. **dirty 게이트** — task worktree 가 깨끗해야 함([`run._is_dirty_excluding_okstra`](../../../scripts/okstra_ctl/run.py:1455)).
|
|
18
|
-
|
|
19
|
-
stage worktree 는 부모 task worktree 하위 디렉터리(`.../<task-id>/stage-<N>/`)에 위치하므로, 부모 입장에서 `git status --short` 가 `?? stage-N/` 로 잡아 **자기 산출물이 자기 dirty 게이트를 막는** 모순이 생긴다([clean gate 제외 목록](../../../scripts/okstra_ctl/worktree.py:411) 에 stage 디렉터리가 없음). 또한 stage 머지는 현재 전적으로 사용자 수동이라, 사용자가 머지를 빠뜨리면 merged 게이트에서도 막힌다.
|
|
20
|
-
|
|
21
|
-
근본 해결은 "stage done commit 을 task 브랜치에 머지하고, 역할이 끝난 stage worktree 를 정리"하는 절차를 okstra 가 수행하는 것이다. 이 절차가 끝나면 두 게이트가 모두 자연 통과한다.
|
|
22
|
-
|
|
23
|
-
## 2. 핵심 원칙
|
|
24
|
-
|
|
25
|
-
### 2.1 자동 머지 원칙 전환
|
|
26
|
-
|
|
27
|
-
[`2026-06-06 §2`](2026-06-06-stage-worktree-isolation-design.md) 는 "okstra 자동 머지 없음 — 머지 충돌 해소 책임을 okstra 에 넣지 않는다"를 못박았다. 본 설계는 이를 **whole-task 통합 경로에 한해 전환**한다: okstra 가 stage done commit 을 task 브랜치에 머지한다. 단 충돌 해소 책임은 여전히 사용자에게 남긴다 — 충돌 시 `merge --abort` 후 중단(§4)하므로, okstra 가 충돌을 해소하지 않는다는 그 spec 의 더 깊은 원칙(octopus 보류)은 유지된다. 즉 "자동 머지"는 **충돌 없는 머지의 자동화**이고, 충돌은 종전대로 사용자 몫이다.
|
|
28
|
-
|
|
29
|
-
### 2.2 2-페이즈 분리 — 머지(브랜치 기준)와 teardown(worktree 기준)
|
|
30
|
-
|
|
31
|
-
머지는 stage 브랜치 ref 와 done commit 만 있으면 되고 worktree 가 필요 없다. teardown 은 worktree 디스크 상태에 좌우된다. 둘을 분리하면 "dirty 라 teardown 은 skip 하되 머지는 이미 됨" 같은 상태를 깔끔히 표현할 수 있다.
|
|
32
|
-
|
|
33
|
-
- **Phase A (머지)**: 브랜치/커밋 기준. worktree 존재 불필요.
|
|
34
|
-
- **Phase B (teardown)**: worktree 디스크 기준. Phase A 가 머지/skip 한 stage 만 대상.
|
|
35
|
-
|
|
36
|
-
### 2.3 코어 1개 + 진입 2개 (single-reference-point)
|
|
37
|
-
|
|
38
|
-
머지+teardown 로직은 신규 모듈 [`scripts/okstra_ctl/stage_integrate.py`](../../../scripts/okstra_ctl/stage_integrate.py) 의 `integrate_stages` 하나가 SSOT다. 두 진입점이 이 코어로 수렴한다.
|
|
39
|
-
|
|
40
|
-
- **자동**: whole-task final-verification prepare 경로([`run.py:1509`](../../../scripts/okstra_ctl/run.py:1509) 의 `else` 분기)에서 `resolve_whole_task_target` 호출 **직전**에 `integrate_stages` 를 호출한다. 기존 `auto_reconcile_best_effort`([run.py:1485](../../../scripts/okstra_ctl/run.py:1485)) 와 같은 prepare 단계에 위치한다.
|
|
41
|
-
- **단독 명령**: `okstra ... integrate-stages` — 사용자가 final-verification 전에 명시 호출. CLI(`okstra.sh`/`cli.sh`)와 Node CLI 가 같은 코어로 패스스루.
|
|
42
|
-
|
|
43
|
-
### 2.4 whole-task 통합과 stage-group handoff 의 상호배타성
|
|
44
|
-
|
|
45
|
-
release-handoff 의 stage-group 모드([`handoff._merge_stages`](../../../scripts/okstra_ctl/handoff.py:173))는 `compute_branch_name` 으로 stage 브랜치를 재구성해 머지하므로 **stage 브랜치 ref 에 의존**한다. 본 설계가 stage 브랜치를 삭제(§3 Phase B)해도 이것이 문제되지 않는 이유:
|
|
46
|
-
|
|
47
|
-
- whole-task 통합을 수행하는 순간 **모든 stage 가 task 브랜치 하나로 합쳐진다.** 이 시점에 "stage 부분집합만 골라 collector 브랜치로 릴리스"하는 stage-group 은 의미를 잃는다.
|
|
48
|
-
- stage-group 을 원하는 사용자는 single-stage final-verification(`--stage N`) → stage-group handoff 경로를 타며, whole-task 통합을 호출하지 않는다.
|
|
49
|
-
- 따라서 whole-task 통합 경로의 stage 브랜치 삭제는 stage-group 워크플로우를 막지 않는다 — 두 경로는 애초에 교차하지 않는다.
|
|
50
|
-
|
|
51
|
-
### 2.5 이력 보존
|
|
52
|
-
|
|
53
|
-
Phase A 머지는 `--no-ff` 로 stage 식별 가능한 머지 커밋을 만든다. stage 원본 커밋들은 머지 커밋을 통해 부모 브랜치에서 reachable하므로, stage 브랜치 ref(포인터)를 삭제해도 커밋은 GC 되지 않고 히스토리로 추적된다. 재작업 시 자식 worktree/브랜치를 다시 발급하면 된다.
|
|
54
|
-
|
|
55
|
-
## 3. 설계
|
|
56
|
-
|
|
57
|
-
### Phase A — 위상순 머지
|
|
58
|
-
|
|
59
|
-
입력: task worktree path, task 브랜치, stage map, `consumers.jsonl` done rows.
|
|
60
|
-
|
|
61
|
-
1. stage map 을 `depends_on` 위상정렬(= `stage_number` 오름차순, plan 이 보장) 순으로 순회.
|
|
62
|
-
2. 각 stage 의 done commit(`head_commit`)이 **이미 task HEAD 의 ancestor**면(`git merge-base --is-ancestor`) **머지 skip** (no-op, `already-merged` 로 기록). 기존 `auto_reconcile` 가 patch-id 로 재배치한 경우도 ancestor 로 잡힌다.
|
|
63
|
-
3. 아니면 `git -C <task-worktree> merge --no-ff --no-edit <stage-branch>` 수행.
|
|
64
|
-
4. **충돌 시**: 해당 stage 의 `git merge --abort` 로 **그 머지만** 원상복구 → `IntegrateError`(= `PrepareError` 로 surface)로 충돌 파일 경로를 보여주고 중단. 위상순 앞서 머지 성공한 선행 stage 는 task 브랜치에 그대로 남으며(재시도 시 §3-#2 의 `already_merged` 로 skip 되어 진전이 보존된다), **Phase B(teardown)는 진입하지 않는다** — 충돌이 하나라도 있으면 어떤 worktree·브랜치도 지우지 않는다(teardown 원자성). 머지 자체는 부분 진행될 수 있으나 재진입이 idempotent 하므로 안전하다. (release-handoff `assemble` 의 exit 2 / `_merge_stages` abort 패턴과 동일)
|
|
65
|
-
|
|
66
|
-
산출: `merged: list[int]`, `already_merged: list[int]`.
|
|
67
|
-
|
|
68
|
-
### Phase B — teardown
|
|
69
|
-
|
|
70
|
-
대상: Phase A 가 `merged` 또는 `already_merged` 로 처리한 stage. (충돌로 중단되면 이 페이즈는 실행되지 않음.)
|
|
71
|
-
|
|
72
|
-
각 stage 에 대해:
|
|
73
|
-
|
|
74
|
-
1. stage worktree 가 **dirty**(`_is_dirty_excluding_okstra` 재사용)면 **skip + 경고** — worktree·브랜치 모두 보류. (done 이후 미커밋 변경 손실 방지)
|
|
75
|
-
2. clean 이면 순서대로:
|
|
76
|
-
- `git worktree remove <stage-worktree-path>`
|
|
77
|
-
- registry 의 `<task-key>#stage-<N>` 키 status 해제([`worktree_registry.release_status`](../../../scripts/okstra_ctl/worktree_registry.py) — branch 슬롯은 **건드리지 않음**, flock 보호)
|
|
78
|
-
- `git branch -d <stage-branch>` — **safe delete**. 부모에 머지 안 된 브랜치는 git 이 거부하므로, Phase A 머지 성공이 삭제의 전제로 자동 강제된다(force `-D` 미사용).
|
|
79
|
-
- branch -d 가 **성공한 뒤에만** branch 인덱스 슬롯 해제([`worktree_registry.free_branch_slot`](../../../scripts/okstra_ctl/worktree_registry.py)). 불변식: "registry 가 free 로 표시한 브랜치 이름은 항상 물리적으로 삭제돼 있다" — 동시 run 이 살아있는 브랜치 이름을 재예약하는 윈도우를 막는다. branch -d 실패 시 슬롯을 그대로 둔 채 경고만 남긴다.
|
|
80
|
-
3. per-stage best-effort: 한 stage 의 teardown 실패가 다른 stage 를 막지 않는다(경고 수집 후 계속).
|
|
81
|
-
|
|
82
|
-
산출: `torn_down: list[int]`, `teardown_skipped: list[(int, reason)]`, `warnings: list[str]`.
|
|
83
|
-
|
|
84
|
-
### 반환 — `IntegrateResult`
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
merged : list[int] # 이번에 머지한 stage
|
|
88
|
-
already_merged : list[int] # 이미 ancestor 라 skip
|
|
89
|
-
torn_down : list[int] # worktree+registry+branch 제거됨
|
|
90
|
-
teardown_skipped : list[(int, str)] # dirty 등으로 보류
|
|
91
|
-
warnings : list[str]
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
final-verification 리포트의 통합 섹션과 단독 명령 출력에 그대로 노출한다.
|
|
95
|
-
|
|
96
|
-
### non-git / degraded 경로
|
|
97
|
-
|
|
98
|
-
[`2026-06-06 §2.1`](2026-06-06-stage-worktree-isolation-design.md) 의 degrade 신호(`skipped-not-git` / `skipped-in-worktree`)와 동일하게, stage worktree 가 발급된 적 없는 환경에서는 `integrate_stages` 가 no-op(빈 `IntegrateResult`)으로 graceful degrade 한다.
|
|
99
|
-
|
|
100
|
-
## 4. 에러 처리 요약
|
|
101
|
-
|
|
102
|
-
| 상황 | 처리 |
|
|
103
|
-
|---|---|
|
|
104
|
-
| stage 간 머지 충돌 | 해당 stage `merge --abort` → `IntegrateError`/`PrepareError` 충돌 경로 동봉, 중단. 선행 머지는 유지(재시도 시 skip), teardown 은 미진입(teardown 원자성) |
|
|
105
|
-
| 선행 stage 미완(done row 없음) | merged 게이트가 별도로 잡음 — 통합 전에 stage 미완으로 중단 |
|
|
106
|
-
| stage worktree dirty | 해당 stage teardown skip + 경고, 머지는 유지 |
|
|
107
|
-
| `git branch -d` 거부(미머지) | 발생 불가(Phase A 머지 성공이 전제). 발생 시 경고로 surface, worktree 는 이미 제거됨. branch 슬롯은 해제하지 않아 살아있는 브랜치 이름이 free 로 표시되지 않음(불변식 유지) |
|
|
108
|
-
| worktree remove 실패 | per-stage 경고, 다음 stage 계속 |
|
|
109
|
-
|
|
110
|
-
## 5. 변경 대상 파일 (seed only)
|
|
111
|
-
|
|
112
|
-
`feedback_okstra_fixes_target_end_users`: `runtime/`·개인 `.claude/` 가 아니라 아래 source 파일에만 가한다.
|
|
113
|
-
|
|
114
|
-
| 파일 | 변경 |
|
|
115
|
-
|---|---|
|
|
116
|
-
| [`scripts/okstra_ctl/stage_integrate.py`](../../../scripts/okstra_ctl/stage_integrate.py) (신규) | `integrate_stages` 코어 + `IntegrateResult` / `IntegrateError` |
|
|
117
|
-
| [`scripts/okstra_ctl/run.py`](../../../scripts/okstra_ctl/run.py:1509) | whole-task final-verification prepare 에서 `integrate_stages` 자동 호출; `integrate-stages` 단독 명령 dispatch |
|
|
118
|
-
| [`scripts/okstra_ctl/worktree.py`](../../../scripts/okstra_ctl/worktree.py) | registry `#stage-N` 키 해제 헬퍼(flock 보호); worktree dirty 검사 재사용 |
|
|
119
|
-
| [`scripts/okstra.sh`](../../../scripts/okstra.sh) · [`scripts/lib/okstra/cli.sh`](../../../scripts/lib/okstra/cli.sh) · Node CLI | `integrate-stages` 진입점 패스스루 |
|
|
120
|
-
| [`prompts/profiles/final-verification.md`](../../../prompts/profiles/final-verification.md) | whole-task 가 통합 머지+teardown 을 수행하는 **mutating** phase 임을 명시 |
|
|
121
|
-
| [`docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md`](2026-06-06-stage-worktree-isolation-design.md) | §2 자동 머지 원칙·§9-#2 teardown 후속 과제가 본 spec 으로 전환·해소됨을 상호 참조 |
|
|
122
|
-
| [`CHANGES.md`](../../../CHANGES.md) | `사용자 영향:` 항목(한국어) |
|
|
123
|
-
|
|
124
|
-
## 6. declaration ↔ enforcement (okstra `CLAUDE.md` rule 5)
|
|
125
|
-
|
|
126
|
-
- "충돌 시 전체 중단" 의 강제 지점은 Phase A 의 `merge --abort` + `IntegrateError` 예외다(프로파일 문구가 아니라 런타임).
|
|
127
|
-
- "머지된 stage 만 브랜치 삭제" 의 강제 지점은 `git branch -d`(safe delete)의 git 자체 거부다.
|
|
128
|
-
- "dirty teardown 금지" 의 강제 지점은 Phase B 의 `_is_dirty_excluding_okstra` 검사다.
|
|
129
|
-
- "free 로 표시한 브랜치 이름은 항상 물리 삭제돼 있다" 의 강제 지점은 `_teardown_stage` 의 호출 순서다 — `release_status`(status 만) → `git branch -d` → 성공 시에만 `free_branch_slot`. 실패하면 슬롯을 그대로 둔다. 회귀 테스트는 `tests/run/test_stage_integrate.py::test_teardown_warns_when_branch_delete_fails`.
|
|
130
|
-
|
|
131
|
-
## 7. 검증 시나리오 (테스트)
|
|
132
|
-
|
|
133
|
-
| # | 시나리오 | 기대 |
|
|
134
|
-
|---|----------|------|
|
|
135
|
-
| I1 | 미머지 stage 1·2·3, 충돌 없음 | 위상순 `--no-ff` 머지, 모두 `merged`, worktree+registry+branch 제거, dirty 게이트 통과 |
|
|
136
|
-
| I2 | 일부 stage 이미 ancestor | 해당 stage `already_merged`(머지 skip), teardown 은 수행 |
|
|
137
|
-
| I3 | stage 1 머지 성공 후 stage 2 충돌 | stage 2 `merge --abort`, stage 1 머지는 task 브랜치에 유지, `IntegrateError`, teardown 전혀 미실행. 재시도 시 stage 1 `already_merged` |
|
|
138
|
-
| I4 | stage 3 worktree dirty | stage 1·2 teardown, stage 3 는 머지 유지 + teardown skip + 경고 |
|
|
139
|
-
| I5 | `git branch -d` 대상이 부모에 머지됨 | 정상 삭제(force 아님) |
|
|
140
|
-
| I6 | non-git / stage worktree 미발급 | no-op `IntegrateResult`, 오류 없음 |
|
|
141
|
-
| I7 | 단독 명령 `integrate-stages` 와 final-verification 자동 경로 | 동일 `integrate_stages` 코어 호출(결과 동등) |
|
|
142
|
-
| I8 | 통합 후 whole-task final-verification 재진입 | merged·dirty 게이트 모두 통과, 통합 섹션이 결과 보고 |
|
|
143
|
-
| I9 | `git branch -d` 실패(인위적 미머지 브랜치) | 경고 surface, `torn_down` 제외, branch 슬롯 free 안 함, 브랜치 보존 |
|
|
144
|
-
| I10 | `git worktree remove` 실패(dirty 게이트 통과했으나 untracked 잔존) | 경고 surface, `torn_down` 제외, worktree 보존, 이후 단계 미진행 |
|
|
145
|
-
| I11 | registry 에 stage-key 없음(`get_stage_row` None) | 경고 surface, 해당 stage 머지 skip(`merged`/`already_merged` 모두 제외) |
|
|
@@ -1,125 +0,0 @@
|
|
|
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 함수와 동일 함수로 통일(라벨↔경로 일치 보장).
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# release-handoff `local checkout` 액션 설계
|
|
2
|
-
|
|
3
|
-
- 작성일: 2026-06-21
|
|
4
|
-
- 상태: 설계 확정 대기(사용자 리뷰)
|
|
5
|
-
- 관련 코드: [release-handoff.md](../../../prompts/profiles/release-handoff.md), [handoff.py](../../../scripts/okstra_ctl/handoff.py), [worktree.py](../../../scripts/okstra_ctl/worktree.py), [worktree_registry.py](../../../scripts/okstra_ctl/worktree_registry.py)
|
|
6
|
-
|
|
7
|
-
## 1. 배경 / 문제
|
|
8
|
-
|
|
9
|
-
okstra 의 task 작업은 사용자 repo 의 **격리된 git worktree**(`~/.okstra/worktrees/<project-id>/<task-group>/<task-id>/`, [worktree.py:764](../../../scripts/okstra_ctl/worktree.py:764) 의 `git worktree add -b`)에서 진행되고, 결과는 task 브랜치에 커밋된다. final-verification(whole-task)이 모든 stage 를 이 task 브랜치에 머지한 뒤에도, 사용자가 그 결과를 **자기 메인 작업 디렉터리에서 로컬 테스트** 하려면 불편이 있다:
|
|
10
|
-
|
|
11
|
-
- task 브랜치는 okstra 워크트리가 점유 중이라, 메인 디렉터리에서 `git checkout <task-branch>` 가 git 규칙(한 브랜치는 한 워크트리만 체크아웃)으로 막힌다.
|
|
12
|
-
- 결국 사용자가 `~/.okstra/worktrees/...` 폴더로 직접 `cd` 해서 테스트해야 하는데, 이게 번거롭다.
|
|
13
|
-
|
|
14
|
-
release-handoff 에는 현재 `local only` / `push + PR` / `skip` 세 액션이 있지만, "검증된 브랜치를 메인 작업 디렉터리로 가져와 로컬 테스트 가능 상태로 만드는" 경로가 없다.
|
|
15
|
-
|
|
16
|
-
## 2. 목표 / 비목표
|
|
17
|
-
|
|
18
|
-
**목표**
|
|
19
|
-
- release-handoff 에 `local checkout` 액션을 추가해, 확인 후 okstra task-key 워크트리를 제거하고 task 브랜치를 **메인 워크트리에서 checkout** 해 로컬 테스트가 가능하게 한다.
|
|
20
|
-
|
|
21
|
-
**비목표 (설계상 명시적 제외)**
|
|
22
|
-
- base 브랜치(main/master/preprod 등)로의 **자동 머지는 하지 않는다.** main 통합은 PR 리뷰를 거쳐야 하므로 종전대로 `push + PR` 만 담당한다. `local checkout` 은 base 를 건드리지 않는다.
|
|
23
|
-
- stage-group 모드는 v1 범위에서 제외(§5).
|
|
24
|
-
|
|
25
|
-
## 3. 결정사항 요약 (사용자 합의)
|
|
26
|
-
|
|
27
|
-
| 항목 | 결정 |
|
|
28
|
-
|---|---|
|
|
29
|
-
| 트리거 지점 | release-handoff 의 Action selection 에 신규 액션 추가 |
|
|
30
|
-
| 실행 범위 | okstra 가 확인받고 worktree remove + 메인 `git checkout` 까지 **대신 실행** |
|
|
31
|
-
| 안전장치 | 메인 워크트리 dirty 면 중단·경고 |
|
|
32
|
-
| 액션 구성 | `local checkout`(신규, 첫 번째) / `push + PR` / `skip`. 기존 `local only` 제거(skip 과 중복) |
|
|
33
|
-
| 모드 범위 | **whole-task 모드 전용** |
|
|
34
|
-
| 메커니즘 | [handoff.py](../../../scripts/okstra_ctl/handoff.py) 에 `okstra handoff local-checkout` 서브커맨드 추가 |
|
|
35
|
-
|
|
36
|
-
## 4. 액션 선택 재편 (release-handoff.md)
|
|
37
|
-
|
|
38
|
-
기존 "User interaction protocol → Action selection" 의 3지선다를 다음으로 교체:
|
|
39
|
-
|
|
40
|
-
1. **`local checkout`** — 검증된 task 브랜치를 메인 워크트리로 가져와 로컬 테스트 가능 상태로 만든다(push/PR 없음).
|
|
41
|
-
2. **`push + PR`** — feature 브랜치 push + PR 생성(기존 흐름 그대로).
|
|
42
|
-
3. **`skip`** — 어떤 git 명령도 없이 release-handoff 를 의도적으로 건너뛴 것으로 기록하고 final-report self-review 로 라우팅(기존 그대로).
|
|
43
|
-
|
|
44
|
-
`local only` 는 제거한다 — "git 안 건드리고 기록만 하고 끝"이라는 점에서 `skip` 과 사실상 중복이기 때문. skip 의 하위-단계 cancel 경로와 verdict 게이트는 유지되므로 잘못 진입 시 탈출 경로는 남는다.
|
|
45
|
-
|
|
46
|
-
## 5. 모드 범위 (whole-task 전용)
|
|
47
|
-
|
|
48
|
-
- `HANDOFF_MODE == whole-task` 일 때만 `local checkout` 을 제공한다. whole-task 의 deliverable 은 task 브랜치 그 자체라 바로 checkout 가능.
|
|
49
|
-
- `HANDOFF_MODE == stage-group` 에서 사용자가 `local checkout` 을 고르면, *"`local checkout` 은 whole-task 모드에서만 지원됩니다"* 안내 후 액션 선택을 다시 받는다(stage-group 은 collector 브랜치를 `assemble` 로 별도 생성해야 해 복잡도가 큼 — YAGNI).
|
|
50
|
-
|
|
51
|
-
## 6. 메커니즘 — `okstra handoff local-checkout`
|
|
52
|
-
|
|
53
|
-
[handoff.py](../../../scripts/okstra_ctl/handoff.py) 에 서브커맨드를 추가한다. release-handoff 프로필은 확인(AskUserQuestion)을 받은 뒤 이 명령 **하나만** 호출한다.
|
|
54
|
-
|
|
55
|
-
### 6.1 인자
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
okstra handoff local-checkout \
|
|
59
|
-
--project-root <dir> --project-id <id> --task-group <g> --task-id <t>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
브랜치 이름과 okstra 워크트리 경로는 인자로 받지 않고 **registry 의 task-key 행(SSOT)** 에서 해소한다(`stage_number` 없음 = task-key). 잘못된 브랜치 전달 위험을 없앤다.
|
|
63
|
-
|
|
64
|
-
### 6.2 단계 (모든 git 은 메인 워크트리 cwd 에서 실행)
|
|
65
|
-
|
|
66
|
-
self-removal 문제(release-handoff 는 okstra task-key 워크트리 안에서 실행됨, §8)를 피하려고, 헬퍼는 자기 cwd 를 **메인 워크트리**로 두고 모든 git 을 거기서 실행한다.
|
|
67
|
-
|
|
68
|
-
1. **registry 해소**: task-key 행에서 `okstra_wt`(worktree_path)·`branch` 를 읽는다. 행이 없거나 비면 exit 1 + 사유.
|
|
69
|
-
2. **메인 워크트리 해소**: [worktree.main_worktree_path(project_root)](../../../scripts/okstra_ctl/worktree.py:306).
|
|
70
|
-
3. **메인 dirty 체크**: [is_dirty_excluding_okstra(main_wt)](../../../scripts/okstra_ctl/worktree.py:465) 가 dirty 면 exit 1 + 경고(사용자 변경 보호). `.okstra` 잡음은 제외.
|
|
71
|
-
4. **okstra 워크트리 제거**: `git -C <main_wt> worktree remove --force <okstra_wt>`. **브랜치는 보존**(teardown 의 `branch -d` 와 다름 — checkout 대상이므로 절대 삭제 안 함). `--force` 는 추적되지 않은 okstra 산출물(심링크/스냅샷, 재생성 가능)만 정리한다. 실패면 exit 1 + stderr.
|
|
72
|
-
5. **registry status 해제**: [worktree_registry.release_status(...)](../../../scripts/okstra_ctl/worktree_registry.py) (task-key). **branch 슬롯은 free 하지 않는다** — 브랜치가 메인에 살아있으므로, "registry 가 free 로 표시한 브랜치는 항상 물리 삭제돼 있다"는 불변식을 지킨다([free_branch_slot](../../../scripts/okstra_ctl/worktree_registry.py) 호출하지 않음).
|
|
73
|
-
6. **메인에서 checkout**: `git -C <main_wt> checkout <branch>`. 실패면(예: 동일 이름 충돌) exit 1 + 사유. 이 시점이면 워크트리는 이미 제거됐고 브랜치는 어디에도 체크아웃돼 있지 않으므로 사용자가 수동 `git checkout` 으로 복구 가능 — 사유에 그 안내를 포함.
|
|
74
|
-
7. **결과 JSON 출력**: `{ "branch": ..., "mainWorktreePath": ..., "removedWorktree": ..., "status": "checked-out" }`.
|
|
75
|
-
|
|
76
|
-
함수는 50줄 이하로 유지(필요 시 dirty/remove/checkout 단계 헬퍼 분리).
|
|
77
|
-
|
|
78
|
-
### 6.3 exit code
|
|
79
|
-
|
|
80
|
-
- `0` 성공.
|
|
81
|
-
- `1` 전제 위반/실패(메인 dirty, registry 행 없음, worktree remove 실패, checkout 실패). 사유는 stderr.
|
|
82
|
-
|
|
83
|
-
## 7. 프로필의 `local checkout` 핸들러 흐름 (release-handoff.md)
|
|
84
|
-
|
|
85
|
-
1. whole-task 게이트(§5). stage-group 이면 안내 후 액션 재선택.
|
|
86
|
-
2. **확인 프롬프트**: 무엇을 할지 명시("okstra 워크트리 제거 + 메인에서 `<branch>` checkout, base 브랜치는 안 건드림")하고 진행/취소.
|
|
87
|
-
3. `okstra handoff local-checkout ...` 호출. exit 1 이면 사유를 보여주고 액션 선택으로 돌아감.
|
|
88
|
-
4. 성공 시, **lead 의 cwd(구 okstra 워크트리)는 사라진다** → 이후 모든 단계(final-report 작성 등)는 **메인 워크트리 절대경로 기준**으로 수행하도록 프로필에 명시.
|
|
89
|
-
5. final-report self-review 로 라우팅.
|
|
90
|
-
|
|
91
|
-
## 8. 안전장치 / 엣지 케이스
|
|
92
|
-
|
|
93
|
-
- **self-removal**: release-handoff 는 `provision_task_worktree` 로 task-key 워크트리 안에서 실행([run.py:1920](../../../scripts/okstra_ctl/run.py:1920)). 헬퍼가 메인 워크트리 cwd 에서 git 을 실행하므로 자기 자신을 제거하지 않는다. 호출 후 lead 는 메인으로 cwd 를 옮긴다(§7.4).
|
|
94
|
-
- **메인 dirty**: §6.2-3 에서 중단. 사용자 미커밋 변경을 절대 덮어쓰지 않는다.
|
|
95
|
-
- **브랜치 보존 불변식**: §6.2-5. branch 슬롯을 free 하지 않아 동시 run 이 살아있는 브랜치 이름을 재예약하지 못한다.
|
|
96
|
-
- **checkout 실패**: §6.2-6. 부분 상태(워크트리 제거됨)에 대한 수동 복구 안내 포함.
|
|
97
|
-
- **base 브랜치 보호**: `local checkout` 은 base 를 절대 건드리지 않는다(비목표). 메인이 마침 base 브랜치를 체크아웃 중이면 checkout 으로 task 브랜치로 전환될 뿐 base 브랜치 ref 는 불변.
|
|
98
|
-
|
|
99
|
-
## 9. 영향 파일
|
|
100
|
-
|
|
101
|
-
- [prompts/profiles/release-handoff.md](../../../prompts/profiles/release-handoff.md): 액션 선택 재편 + `local checkout` 핸들러 + final-report 5.6 기록 지시.
|
|
102
|
-
- [scripts/okstra_ctl/handoff.py](../../../scripts/okstra_ctl/handoff.py): `local-checkout` 서브커맨드 + argparse 등록.
|
|
103
|
-
- [tests/handoff/](../../../tests/handoff/): 헬퍼 테스트(워크트리 fixture → 실행 → 메인 checkout·워크트리 제거·브랜치 보존·dirty 중단 검증).
|
|
104
|
-
- [CHANGES.md](../../../CHANGES.md): `사용자 영향:` 항목.
|
|
105
|
-
- `runtime/` 은 빌드 산출물 — 직접 수정 금지. 작업 후 `npm run build`.
|
|
106
|
-
|
|
107
|
-
## 10. 테스트 계획
|
|
108
|
-
|
|
109
|
-
- **성공 경로**: task-key 워크트리가 있는 fixture repo 에서 헬퍼 실행 → 메인이 task 브랜치 체크아웃됨 + okstra 워크트리 디렉터리 제거됨 + 브랜치 ref 보존됨 + registry status `released`·branch 슬롯 유지.
|
|
110
|
-
- **메인 dirty 중단**: 메인에 미커밋 변경 → exit 1, 워크트리·브랜치 불변.
|
|
111
|
-
- **registry 행 없음**: exit 1.
|
|
112
|
-
- **checkout 충돌**: 메인에 동일 이름 브랜치 선점 → exit 1, 사유 출력.
|
|
113
|
-
- CLI 스모크: `node bin/okstra handoff --help` 에 `local-checkout` 노출.
|