okstra 0.102.4 → 0.104.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +3 -2
- package/README.md +3 -2
- package/docs/for-ai/skills/okstra-brief.md +18 -2
- package/docs/for-ai/skills/okstra-run.md +9 -1
- package/docs/kr/cli.md +1 -0
- package/docs/project-structure-overview.md +9 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/profiles/_common-contract.md +4 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/improvement-discovery.md +2 -0
- package/runtime/prompts/profiles/requirements-discovery.md +4 -0
- package/runtime/prompts/wizard/prompts.ko.json +1 -1
- package/runtime/python/okstra_ctl/manager_cli.py +231 -0
- package/runtime/python/okstra_ctl/manager_launch.py +256 -0
- package/runtime/python/okstra_ctl/manager_paths.py +101 -0
- package/runtime/python/okstra_ctl/manager_store.py +320 -0
- package/runtime/python/okstra_ctl/manager_sync.py +152 -0
- package/runtime/python/okstra_ctl/wizard.py +105 -15
- package/runtime/skills/okstra-brief/SKILL.md +32 -7
- package/runtime/skills/okstra-manager/SKILL.md +44 -0
- package/runtime/skills/okstra-run/SKILL.md +2 -2
- package/runtime/templates/reports/brief.template.md +25 -0
- package/runtime/validators/validate-brief.py +128 -0
- package/src/cli-registry.mjs +7 -0
- package/src/commands/manager.mjs +57 -0
- package/src/lib/skill-catalog.mjs +1 -0
package/README.kr.md
CHANGED
|
@@ -43,7 +43,7 @@ okstra/ npm 패키지 = repo 루트
|
|
|
43
43
|
├── tools/build.mjs runtime/ 동기화 스크립트 (prepack 에서 호출)
|
|
44
44
|
├── runtime/ gitignored 설치 payload; ~/.okstra 로 복사
|
|
45
45
|
├── scripts/ python + bash 런타임 소스
|
|
46
|
-
├── skills/ public 스킬 마크다운 소스 (사용자 노출
|
|
46
|
+
├── skills/ public 스킬 마크다운 소스 (사용자 노출 8종)
|
|
47
47
|
├── agents/ worker agent 마크다운 소스
|
|
48
48
|
├── prompts/, schemas/, templates/, validators/
|
|
49
49
|
├── docs/kr/ 한국어 상세 매뉴얼 (architecture.md, cli.md)
|
|
@@ -77,7 +77,7 @@ okstra/ npm 패키지 = repo 루트
|
|
|
77
77
|
|
|
78
78
|
~/.claude/skills/ Claude Code 가 자동 인식 (`~/.claude` 가 있을 때)
|
|
79
79
|
~/.agents/skills/ Agent 호환 host 가 인식 (`install` 이 생성)
|
|
80
|
-
└── okstra-*/SKILL.md 사용자 노출 스킬
|
|
80
|
+
└── okstra-*/SKILL.md 사용자 노출 스킬 8종만 (setup/brief/run/memory/inspect/schedule/container/manager)
|
|
81
81
|
|
|
82
82
|
~/.claude/agents/ Claude Code 가 자동 인식 (`~/.claude` 가 있을 때)
|
|
83
83
|
└── {claude,codex,antigravity,report-writer}-worker.md worker subagent 정의
|
|
@@ -163,6 +163,7 @@ Claude Code 세션 안에서 사용하는 슬래시 커맨드:
|
|
|
163
163
|
| `/okstra-inspect` | 통합 read-side 스킬. sub-command: `status` (phase / 상태, workStatus 설정), `history` (과거 task / re-run / resume), `report` (final-report 조회·읽기), `time` (소요 시간 breakdown), `logs` (wrapper log sidecar 조회·정리 제안), `cost` (task bundle 컨텍스트/읽기 비용), `errors` (run 에러 로그를 리포트로 집계), `error-zip` (cross-project 에러 로그를 익명화 zip 으로 수집·클러스터 요약), `recap` (run 간 전/후 요약 + task 의 `.okstra` 산출물 위 자유 Q&A) |
|
|
164
164
|
| `/okstra-schedule` | task-group 전체에 대한 작업 계획표 생성 |
|
|
165
165
|
| `/okstra-container-build` | 검증 완료된 task 의 코드를 로컬 docker compose 그룹으로 배포하고 컨테이너별 로그를 감시 (sub-command: `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
166
|
+
| `/okstra-manager` | 여러 프로젝트에 걸친 okstra task 를 manager-owned plan, assignment, 단방향 project sync snapshot, status, child launch context packet 으로 조정 |
|
|
166
167
|
| `/okstra-setup` | 프로젝트별 부트스트랩 (§3.2) |
|
|
167
168
|
|
|
168
169
|
lead 운영 계약과 지원 계약 — context-loader, team-contract, convergence, report-writer, 그리고 coding-preflight 팩 — 은 더 이상 agent 스킬로 설치되지 않습니다. okstra 런타임 리소스로 `~/.okstra/prompts/` (`prompts/lead/*.md`, `prompts/coding-preflight/*`) 아래에 배치되고, generated launch prompt 가 lead 에게 절대 경로를 전달합니다. 재설치 시 agent skill home 에 남은 과거 복사본은 prune 되므로 슬래시 커맨드로 노출되지 않습니다.
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ okstra/ npm package = repo root
|
|
|
43
43
|
├── tools/build.mjs runtime/ sync script (invoked by prepack)
|
|
44
44
|
├── runtime/ gitignored install payload copied to ~/.okstra
|
|
45
45
|
├── scripts/ python + bash runtime sources
|
|
46
|
-
├── skills/ public skill markdown sources (
|
|
46
|
+
├── skills/ public skill markdown sources (8 user-facing skills)
|
|
47
47
|
├── agents/ worker agent markdown sources
|
|
48
48
|
├── prompts/, schemas/, templates/, validators/
|
|
49
49
|
├── tests/, tests-e2e/
|
|
@@ -75,7 +75,7 @@ okstra/ npm package = repo root
|
|
|
75
75
|
|
|
76
76
|
~/.claude/skills/ discovered automatically by Claude Code (if ~/.claude exists)
|
|
77
77
|
~/.agents/skills/ discovered by Agent-compatible hosts (created by install)
|
|
78
|
-
└── okstra-*/SKILL.md user-facing skills only (setup/brief/run/memory/inspect/schedule/container)
|
|
78
|
+
└── okstra-*/SKILL.md user-facing skills only (setup/brief/run/memory/inspect/schedule/container/manager)
|
|
79
79
|
|
|
80
80
|
~/.claude/agents/ discovered automatically by Claude Code (if ~/.claude exists)
|
|
81
81
|
└── {claude,codex,antigravity,report-writer}-worker.md subagent definitions
|
|
@@ -161,6 +161,7 @@ User-facing slash commands inside a Claude Code session:
|
|
|
161
161
|
| `/okstra-inspect` | Unified read-side. Sub-commands: `status` (phase / state, workStatus update), `history` (past runs, re-run, resume), `report` (find/read final-report), `time` (elapsed-time breakdown), `logs` (wrapper log sidecar inventory + cleanup), `cost` (task bundle context/read cost), `errors` (aggregate run error logs into a report), `error-zip` (collect cross-project error logs into an anonymized zip and summarize clusters), `recap` (run-to-run before/after summary + free-form Q&A over a task's `.okstra` artifacts) |
|
|
162
162
|
| `/okstra-schedule` | Generate a work schedule for an entire task-group |
|
|
163
163
|
| `/okstra-container-build` | Deploy a verified task's code as a local docker compose group and watch each container's logs (sub-commands: `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
164
|
+
| `/okstra-manager` | Coordinate cross-project okstra tasks through manager-owned plans, assignments, one-way project sync snapshots, status, and child launch context packets |
|
|
164
165
|
| `/okstra-setup` | Per-project bootstrap (§3.2) |
|
|
165
166
|
|
|
166
167
|
The lead operating contract and its support contracts — context-loader, team-contract, convergence, report-writer, and the coding-preflight pack — are **not** installed as agent skills. They ship as okstra runtime resources under `~/.okstra/prompts/` (`prompts/lead/*.md` and `prompts/coding-preflight/*`) and the generated launch prompt hands the lead their absolute paths. Reinstalling prunes any earlier copies of these from the agent skill homes, so they never appear as slash commands.
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
4. `intent-inference`는 `Open Questions`의 `intent-check:`와 짝을 이룬다. 이 관계는 `validators/validate-brief.py`가 검사한다.
|
|
35
35
|
5. unresolved `terminology-mapping`은 `Open Questions`의 `terminology:`와 짝을 이룬다. 이 관계도 validator가 검사한다.
|
|
36
36
|
6. reporter만 답할 수 있는 질문은 Step 6.5에서 모아 `## Reporter Confirmations`에 verbatim으로 기록한다.
|
|
37
|
-
7.
|
|
37
|
+
7. 티켓 분리/연결/순서 관계는 `## Related Task Graph`의 구조화된 table에 둔다. parent-id만으로 작업 순서를 추론하지 않는다.
|
|
38
|
+
8. 모든 okstra-owned write는 `<PROJECT_ROOT>/.okstra/` 안에 둔다. 외부 파일은 reporter가 source로 명시한 경우에만 읽는다.
|
|
38
39
|
|
|
39
40
|
## Preflight
|
|
40
41
|
|
|
@@ -61,10 +62,24 @@ runtime이나 project setup이 없으면 `/okstra-setup`을 안내하고 멈춘
|
|
|
61
62
|
티켓에 child/sub-task가 있으면 parent에서 한 번만 tree 처리 방식을 묻는다.
|
|
62
63
|
|
|
63
64
|
- Full tree: descendant별 brief 생성.
|
|
64
|
-
- Parent only: child key/URL
|
|
65
|
+
- Parent only: child key/URL을 Related Artifacts에 두고 `Related Task Graph`에는 `parent-of` edge를 남긴다.
|
|
65
66
|
- Selected: 선택한 direct child branch만 재귀 처리.
|
|
66
67
|
|
|
67
68
|
재귀 시 visited set은 `<tracker>:<ticket-id>`로 관리하고, 재실행에서는 기존 brief frontmatter의 `ticket-id` + `source-type`으로 reseed한다.
|
|
69
|
+
Full tree 또는 Selected로 여러 brief를 만들면 모든 생성 brief에 같은 `Related Task Graph`를 복사한다. 그래야 child brief 하나만 downstream phase에 전달돼도 split topology, 선행/후행 관계, 중복 방지 신호가 유지된다.
|
|
70
|
+
|
|
71
|
+
`Related Task Graph` table schema:
|
|
72
|
+
|
|
73
|
+
| Column | Meaning |
|
|
74
|
+
|---|---|
|
|
75
|
+
| From | task key, brief id, tracker id, or URL |
|
|
76
|
+
| Relation | `parent-of`, `child-of`, `depends-on`, `blocks`, `blocked-by`, `follow-up-of`, `split-from`, `duplicates`, `related-to` |
|
|
77
|
+
| To | task key, brief id, tracker id, or URL |
|
|
78
|
+
| Direction | `directed` or `undirected` |
|
|
79
|
+
| Source | tracker linked issue, task-list checkbox, reporter statement, manual split, prior okstra task |
|
|
80
|
+
| Impact | downstream phase가 보존해야 할 의미 |
|
|
81
|
+
|
|
82
|
+
`depends-on`, `blocks`, parent/child, follow-up, split 관계는 `directed`다. `duplicates`, `related-to`는 `undirected`다. source가 없는 관계는 만들지 않는다.
|
|
68
83
|
|
|
69
84
|
### Codebase scan
|
|
70
85
|
|
|
@@ -210,6 +225,7 @@ validators/validate-brief.sh "<PROJECT_ROOT>/.okstra/briefs" --briefs-root "<PRO
|
|
|
210
225
|
```
|
|
211
226
|
|
|
212
227
|
실패하면 cited brief를 고치고 재실행한다. validator가 없을 때만 manual checklist로 대체한다.
|
|
228
|
+
validator는 `Related Task Graph`가 있으면 table header, relation enum, direction enum, directed/undirected mismatch도 검사한다.
|
|
213
229
|
|
|
214
230
|
## 완료 메시지
|
|
215
231
|
|
|
@@ -105,6 +105,15 @@ okstra wizard init --state-file /tmp/okstra-wizard/state.json --project-root /ab
|
|
|
105
105
|
|
|
106
106
|
중요: wizard가 제공한 option을 줄이거나 숨기거나 "추천 + 직접 입력"으로 재구성하지 않는다. wizard의 `options[]`가 완전한 선택지다.
|
|
107
107
|
|
|
108
|
+
## brief 후보 정렬
|
|
109
|
+
|
|
110
|
+
entry phase(`requirements-discovery`, `error-analysis`, `improvement-discovery`)의 brief 선택은 wizard가 처리한다.
|
|
111
|
+
|
|
112
|
+
- task-group 후보는 task catalog의 최근 사용 이력과 `.okstra/briefs/<group>/` 아래 최근 brief 생성/수정 시각을 합산해 최신순으로 보여준다.
|
|
113
|
+
- brief 파일 후보는 선택된 group의 `.okstra/briefs/<task-group>/**/*.md` 안에서 고른다.
|
|
114
|
+
- brief 파일 정렬 키는 `max(파일 생성/수정 시각, 해당 brief를 사용한 task catalog updatedAt)`이다.
|
|
115
|
+
- direct input은 항상 마지막이다.
|
|
116
|
+
|
|
108
117
|
## confirm step
|
|
109
118
|
|
|
110
119
|
`next.step == "confirm"`이면 먼저 confirmation summary를 가져온다.
|
|
@@ -226,4 +235,3 @@ Took over as Claude lead for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_REL
|
|
|
226
235
|
- `okstra.sh`를 호출해 wizard/render-bundle 경로를 우회하기.
|
|
227
236
|
- render-bundle 전에 사용자가 abort한 state로 render-args를 호출하기.
|
|
228
237
|
- Claude lead prompt를 읽기 전에 phase 작업을 임의로 시작하기.
|
|
229
|
-
|
package/docs/kr/cli.md
CHANGED
|
@@ -633,6 +633,7 @@ chmod +x ~/.local/bin/okstra-ctl
|
|
|
633
633
|
| `okstra check-project [--json]` | 현재 프로젝트가 등록되었는지 검증 |
|
|
634
634
|
| `okstra config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | 영구 설정 관리 (예: `pr-template-path`). 원자적 JSON 쓰기 |
|
|
635
635
|
| `okstra memory <add\|list\|search\|show\|archive>` | `~/.okstra/memory-book` 전역 대화 메모리 관리. 프로젝트 `.okstra/` 와 분리된 user-home 저장소이며, `okstra 에 정리해서 보관해` 자연어 스킬의 CLI 기반 |
|
|
636
|
+
| `okstra manager <init\|discover-projects\|new\|task>` | 여러 프로젝트의 okstra task 를 manager-owned context 로 묶는 공개 CLI. `new project`, `new task-group`, `new task` 로 manager 계획을 만들고, `task assign` / `task note` / `task sync` / `task status` / `task run` 으로 프로젝트별 할당과 snapshot 을 관리한다. child task key 는 공개 문서에서 `project-id:task-group:task-id` 전체 형식을 사용하며, `task run` 은 child launch context packet 을 JSON 으로 반환한다 |
|
|
636
637
|
| `okstra migrate [--apply] [--cwd <dir>] [--quiet]` | 한 번만 실행: 프로젝트 산출물 루트를 `.project-docs/okstra/` → `.okstra/` 로 이동. 기본 dry-run, `--apply` 로 실제 실행. `git mv` (git worktree) + 빈 `.project-docs/` 제거 + `<PROJECT>/CLAUDE.md` import 라인 + `.gitignore` + `~/.okstra/{recent,active}.jsonl` + `~/.okstra/worktrees/registry.json` 의 해당 프로젝트 row 동기화. 이미 `.okstra/` 가 존재하거나 레거시 디렉토리가 없으면 exit 1 로 거부. v0.x 말까지 유지 후 제거 예정 |
|
|
637
638
|
| `okstra task-list [--project-root <path>]` | `list_project_tasks` + `read_latest_task` 결과를 합쳐 task 카탈로그 + 최근 task 를 JSON 으로 반환 |
|
|
638
639
|
| `okstra task-show <task-key> [--project-root <path>]` | task-manifest.json 의 workflow / phase / status 요약 |
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
- Node CLI entrypoint: `bin/okstra`
|
|
29
29
|
- Python orchestration authority: `scripts/okstra_ctl/run.py::prepare_task_bundle`
|
|
30
30
|
- lifecycle: `requirements-discovery → error-analysis → implementation-planning → implementation → final-verification → release-handoff`
|
|
31
|
-
- installed skills:
|
|
31
|
+
- installed skills: 8개
|
|
32
32
|
- worker agents: `claude`, `codex`, `antigravity`, `report-writer`
|
|
33
33
|
- final report SSOT: `schemas/final-report-v1.0.schema.json` + `*.data.json`
|
|
34
34
|
|
|
@@ -54,7 +54,7 @@ okstra/
|
|
|
54
54
|
│ ├── okstra_vendor/ vendored Jinja2 / MarkupSafe
|
|
55
55
|
│ ├── lib/okstra/ Bash helpers for okstra.sh
|
|
56
56
|
│ └── lib/okstra-ctl/ Bash control-center subcommands
|
|
57
|
-
├── skills/ Claude Code skills (
|
|
57
|
+
├── skills/ Claude Code skills (8)
|
|
58
58
|
├── agents/ lead SKILL.md + worker agent specs
|
|
59
59
|
├── prompts/ launch template, phase profiles, wizard prompt JSON
|
|
60
60
|
├── schemas/ JSON schema for final-report data.json
|
|
@@ -124,7 +124,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
124
124
|
- `runtime/python/*` → `~/.okstra/lib/python/`
|
|
125
125
|
- `runtime/bin/*` → `~/.okstra/bin/`
|
|
126
126
|
- `runtime/templates/*` → `~/.okstra/templates/`
|
|
127
|
-
- `runtime/skills/<name>` (the
|
|
127
|
+
- `runtime/skills/<name>` (the eight user-facing skills only) → `~/.agents/skills` always, plus `~/.claude/skills` when `~/.claude` exists
|
|
128
128
|
- `runtime/prompts/*` → `~/.okstra/prompts/` (lead contracts under `prompts/lead/`, coding-preflight pack under `prompts/coding-preflight/`)
|
|
129
129
|
- `runtime/agents/workers/*.md` → `~/.claude/agents/*-worker.md` when `~/.claude` exists
|
|
130
130
|
- install manifests → `~/.okstra/installed-skills.json` (target-aware), `~/.okstra/installed-agents.json`
|
|
@@ -197,7 +197,9 @@ Important modules:
|
|
|
197
197
|
| `stage_targets.py` | Stage readiness/verification 정책 SSOT — 어떤 stage 가 실행 가능한지, 어느 commit 에서 분기하는지, final-verification 이 무엇을 검사하는지 결정 (`consumers.jsonl` 행과 git ancestry 검사를 단일 인터페이스 뒤로 캡슐화). `order_stage_closure` 는 wizard 멀티선택 stage 집합의 의존성 closure 를 Kahn 위상정렬해 `chain-stages` 무인 연쇄 순서를 산출 |
|
|
198
198
|
| `stage_reconcile.py` | stage prepare 흐름 공용 best-effort git reconciliation (`git_reconcile.auto_reconcile` 위임; advisory — 실패는 stderr 보고만, dependency gate 가 권위 판정 유지) |
|
|
199
199
|
| `run_context.py` | Per-task mutex, run context and run-input persistence; `consumers_mutex` helper for atomic `consumers.jsonl` writes |
|
|
200
|
+
| `path_hints.py` | Compact path-hint persistence + legacy context hydration — `run-context` / `active-run-context` 를 schemaVersion `2.0` 의 `identity` + `pathHints` compact schema 로 저장하고, host-side reader 가 읽는 순간 기존 flat path key(`RUN_MANIFEST_RELATIVE_PATH`, `TEAM_STATE_PATH` 등)를 메모리에서 hydrate |
|
|
200
201
|
| `consumers.py` | Append-only `consumers.jsonl` writer + reader — records which `implementation` runs consumed which `implementation-planning` stage |
|
|
202
|
+
| `implementation_outcome.py` | Artifact-derived implementation phase outcome 재조합 — `runs/implementation/carry/stage-<N>.json` + `consumers.jsonl` + 승인 Stage Map 을 읽어 `phaseOutcome.implementation` 도출, 모든 stage 가 pass-grade carry evidence 면 `workflow.nextRecommendedPhase` 를 `final-verification` 으로 보정 (`contract-violated` 감사 정보는 유지) |
|
|
201
203
|
| `paths.py` | Path/sequence computation for task/run artifacts (recap 디렉터리/로그 경로 포함) |
|
|
202
204
|
| `recap.py` | okstra-inspect `recap` facet 의 결정론적 백엔드 — `assemble` 은 timeline 에서 run 간 phase 전이를 조립, `record` 는 `<task-root>/recap/recap-log.jsonl` 에 요약·Q&A append-only 기록 (다른 task 산출물 불변) |
|
|
203
205
|
| `render.py` | task manifest, run manifest, timeline, task index, discovery, team-state, prompt/template render |
|
|
@@ -210,7 +212,9 @@ Important modules:
|
|
|
210
212
|
| `qa_commands.py` | QA command deny-list validation for plans |
|
|
211
213
|
| `pr_template.py` | PR body template resolution for release-handoff |
|
|
212
214
|
| `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Final-report data.json → Markdown → slim/HTML pipeline |
|
|
215
|
+
| `final_report_paths.py`, `report_view_artifacts.py` | Final-report markdown/data.json 쌍 및 생성 view artifact(HTML view, user-responses 디렉터리) 경로 helper SSOT |
|
|
213
216
|
| `wizard.py` | `okstra-run` prompt state machine; user-facing Korean strings live in `prompts/wizard/prompts.ko.json` |
|
|
217
|
+
| `wizard_stage_intent.py` | `okstra-run` wizard 출력의 stage 관련 intent projection — whole-task(`__whole_task__`) vs 단일/멀티 stage 선택을 render-args 로 정규화 (`resolve_wizard_stage_intent`) |
|
|
214
218
|
| `index.py`, `jsonl.py`, `reconcile.py`, `listing.py`, `batch.py`, `backfill.py` | `~/.okstra` run index and history operations |
|
|
215
219
|
| `run_index_row.py` | `~/.okstra` run-index row 의 생성/축약(slim)/복원(hydrate) 단일 참조점 — runId SSOT, projectId raw 보존 |
|
|
216
220
|
| `error_report.py`, `error_log_core.py`, `error_zip.py` | okstra-inspect errors/error-zip facet 백엔드 — `error_log_core` 는 `errors-*.jsonl` 글롭·파싱·집계 read-only 코어, `error_report` 는 errors facet 렌더, `error_zip` 은 cross-project run 디렉터리 수집·allowlist 익명화·클러스터 집계·zip 생성 |
|
|
@@ -280,7 +284,7 @@ Token/cost accounting:
|
|
|
280
284
|
|
|
281
285
|
### 4.10 `skills/`
|
|
282
286
|
|
|
283
|
-
|
|
287
|
+
8 user-facing skills (the only skills `okstra install` copies):
|
|
284
288
|
|
|
285
289
|
| Skill | User-invocable | Role |
|
|
286
290
|
|---|---:|---|
|
|
@@ -290,6 +294,7 @@ Token/cost accounting:
|
|
|
290
294
|
| `okstra-inspect` | yes | Unified read-side — sub-commands `status` (lifecycle + workStatus), `history` (past runs / re-run / resume), `report` (find final-report), `time` (elapsed-time breakdown), `logs` (wrapper log inventory + cleanup), `cost` (task bundle context/read cost) |
|
|
291
295
|
| `okstra-schedule` | yes | Generate task-group schedule |
|
|
292
296
|
| `okstra-container-build` | yes | Non-linear deploy tool — deploy a verified task's code as a docker compose group and watch each container (sub-commands `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
297
|
+
| `okstra-manager` | yes | Coordinate cross-project okstra tasks through manager-owned plans, assignments, sync snapshots, status, and child launch context packets |
|
|
293
298
|
| `okstra-setup` | yes | Install/check runtime and register project |
|
|
294
299
|
|
|
295
300
|
> The former internal skills — `context-loader`, `team-contract`, `convergence`, `report-writer`, `coding-preflight` — and the lead contract are no longer skills. They ship as runtime resources under `prompts/lead/*` and `prompts/coding-preflight/*` (installed to `~/.okstra/prompts/`); see §4.11.
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -74,6 +74,10 @@ profile document.
|
|
|
74
74
|
- `pending` (or field missing) → ABORT analysis; render the Verdict Card with `Verdict Token = blocked` + `Direction = hold` and write a single `## Reporter Confirmation Required` block (no leading number) summarising which rows are pending. The `## 1. Clarification Items` table carries one row per pending item with `Blocks=approval` in `implementation-planning`, otherwise `Blocks=next-phase`. The operator must rerun `okstra-brief` Step 6.5. Do NOT emit `## 0.` for this case — Section 0 is reserved for clarification-response carry-in only.
|
|
75
75
|
`[CONFIRMED <YYYY-MM-DD> → RC-N]` markers on `Open Questions` rows are the per-row signal that the reporter has answered; their answers live verbatim under `## Reporter Confirmations` in the brief.
|
|
76
76
|
- `Source Material` is reporter-verbatim. Do NOT paraphrase, summarize, reorder, or restructure it. Quote it directly when needed.
|
|
77
|
+
- `Related Task Graph` is the structured task-topology handoff. If the section is present and not `_(none)_`, read it before classification, diagnosis, candidate discovery, fan-out, or next-step routing. Preserve the edge direction exactly as written: `From` → `To` is load-bearing for `depends-on`, `blocks`, parent/child, follow-up, and split relations.
|
|
78
|
+
- Valid relations are `parent-of`, `child-of`, `depends-on`, `blocks`, `blocked-by`, `follow-up-of`, `split-from`, `duplicates`, and `related-to`. `duplicates` / `related-to` are undirected; every other relation is directed.
|
|
79
|
+
- Treat `Source` as the edge provenance. Do not invent graph edges from filename similarity, topic overlap, or worker preference. If a needed edge is missing or contradictory, raise a `Clarification Items` row instead of silently reordering tasks.
|
|
80
|
+
- When a phase emits fan-out packets, recommended next steps, root-cause boundaries, or improvement candidates, carry forward any relevant graph edge by referencing its `From`, `Relation`, `To`, and `Impact` values.
|
|
77
81
|
- `Augmentation` entries carry one of four labels — `evidence-link`, `format-conversion`, `terminology-mapping`, `intent-inference`. Treat them as follows:
|
|
78
82
|
- `evidence-link` / `format-conversion` → trust without re-verification.
|
|
79
83
|
- `terminology-mapping` → verify against `<PROJECT_ROOT>/.okstra/glossary.md` (authoritative); raise a `Clarification Items` row if the mapping is missing or contradicts the glossary.
|
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
- Brief consumption (phase-specific addendum — shared rules live in `_common-contract.md` under "Brief handoff contract"):
|
|
12
12
|
- Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows use `Blocks=next-phase`; any unconfirmed inference may be used as a labelled hypothesis only.
|
|
13
13
|
- the reporter's symptom description in `Source Material` is the ground truth for what to reproduce. Do not paraphrase it when stating the symptom in the report; quote it.
|
|
14
|
+
- read `Related Task Graph` before forming root-cause candidates. `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges define upstream/downstream boundaries for the symptom: identify whether this task's failure is caused by, blocks, or merely relates to another task before merging causes.
|
|
14
15
|
- any `intent-inference` augmentation that re-characterises the symptom (e.g. classifying "가끔 안 됨" as "intermittent failure on a specific code path") is a **hypothesis**, not a confirmed symptom. If `[CONFIRMED …]` appears on the matching `intent-check:` row, treat that confirmation as the symptom. Otherwise follow the precondition's `skipped` branch above and keep the inference labelled as a hypothesis in the root-cause analysis.
|
|
15
16
|
- `conversion-block:` rows mean the brief could not map a reporter statement to project vocabulary; never invent the missing mapping in this phase.
|
|
16
17
|
- Diagnosis loop:
|
|
17
18
|
- **Symptom lock:** state the reporter's symptom verbatim, then translate it into one observable failure condition. If no observable condition can be derived from the brief, record that gap as the first blocker instead of guessing.
|
|
18
19
|
- **Reproduction status:** classify the run as `reproduced`, `not-reproduced`, or `blocked-before-repro`. Cite the command/log/file evidence used. If no command can be run safely in this phase, explain the read-only evidence path and the exact material needed next.
|
|
19
20
|
- **Falsifiable cause candidates:** every root-cause candidate must include supporting evidence, the strongest falsifying evidence checked, confidence, and the next diagnostic action that would disprove it. A candidate that cannot be falsified is too vague for this phase.
|
|
21
|
+
- **Graph-aware scope:** a graph edge can explain ordering or duplication, but it is not proof of cause by itself. Cite code/log evidence before claiming an upstream related task caused the current symptom.
|
|
20
22
|
- **Sharp next diagnostic:** end with the single highest-value diagnostic command, log capture, or file inspection that should happen next, plus the expected signal that would confirm or reject the leading cause.
|
|
21
23
|
- **Fix-design boundary:** do not design the implementation fix beyond what is necessary to validate the cause. If the cause is credible, route to `implementation-planning` with the verified evidence; if the cause is still unclear, route to another `error-analysis` run with the next diagnostic.
|
|
22
24
|
- Primary focus areas:
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- the brief's `scan-scope` defines the only paths workers may read for candidate evidence. `out-of-scope` paths MUST be ignored even when the codebase is otherwise reachable.
|
|
16
16
|
- the brief's `candidate-cap` (default 8 if absent, absolute cap 12) bounds the number of rows in `## 5.9 Improvement Candidates`.
|
|
17
17
|
- Apply the shared reporter-confirmation precondition as written. For this phase any unresolved `intent-check:` / `conversion-block:` row uses `Blocks=next-phase`.
|
|
18
|
+
- read `Related Task Graph` before candidate discovery. If a candidate overlaps an existing linked task, preserve the edge in the candidate notes and prefer `related-to` / `follow-up-of` routing over emitting a duplicate standalone task.
|
|
18
19
|
- Primary focus areas:
|
|
19
20
|
- candidate discovery within the lens whitelist
|
|
20
21
|
- per-candidate evidence (path:line) and scope mapping
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
- each worker, before broadening to the remaining lenses, must do one of: (a) produce at least one candidate from its primary pass, or (b) record a no-candidate rationale citing the highest-signal path:line evidence it inspected.
|
|
26
27
|
- two workers' candidates are the same candidate only when they cite the same underlying design/code problem and the same remediation direction. Shared evidence paths alone are not enough to merge; keep distinct failure modes distinct.
|
|
27
28
|
- when a candidate from one worker overlaps another worker's evidence, convergence must classify the relationship as one of: duplicate, broader/narrower, or conflicting. Do not collapse contested candidates just to meet the candidate cap.
|
|
29
|
+
- when a candidate overlaps a brief `Related Task Graph` edge, convergence must classify whether the candidate is a duplicate of, blocked by, or follow-up to the linked task. Do not assign a new task-key without carrying the linked task reference into Recommended Next Steps.
|
|
28
30
|
- Phase 1.5 — Lead reflect-back grilling (runs after Phase 1 context loading and before Phase 4 worker dispatch):
|
|
29
31
|
- Lead inspects scan-scope paths via `ls` / `Grep` / `Read` to map modules, entry points, dependencies, and approximate LOC, and reviews recent git history to note which scan-scope files change most.
|
|
30
32
|
- Lead emits a single reflect-back message covering: (a) understood scope per path (one-line summary), (b) understood meaning of each priority lens in this scope, (c) understood out-of-scope rationale, (d) ordered list of N open questions.
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- Brief consumption (phase-specific addendum — shared rules live in `_common-contract.md` under "Brief handoff contract"):
|
|
12
12
|
- Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows use `Blocks=next-phase`.
|
|
13
13
|
- before classifying (after the precondition passes), scan the brief for every `Open Questions` row prefixed `intent-check:` / `terminology:` / `conversion-block:` and every `Augmentation` entry labelled `intent-inference` / `terminology-mapping`, and resolve OR carry forward each one in this phase.
|
|
14
|
+
- before deciding whether to fan out, read `Related Task Graph`. Use `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges as seed ordering constraints. Do not flatten a directed graph into unrelated tasks.
|
|
14
15
|
- `intent-inference` augmentations whose paired `intent-check:` row carries `[CONFIRMED …]` are treated as **confirmed**; trust the confirmation text in `## Reporter Confirmations` over the original inference if they differ. Unconfirmed `intent-inference` rows under `reporter-confirmations: skipped` follow the precondition's `skipped` branch above.
|
|
15
16
|
- `conversion-block:` rows are explicit "translation failed" signals — never attempt to resolve them by inference here; the precondition above already handled them.
|
|
16
17
|
- Primary focus areas:
|
|
@@ -32,6 +33,9 @@
|
|
|
32
33
|
번호목록(`1. unit-001`)으로 나열한다(생성 뷰; 직접 편집 금지 명시). depends-on 그래프는
|
|
33
34
|
DAG 여야 한다 — `validate_fanout` 가 순환을 검증 실패로 거부하므로, 순환이 생기면
|
|
34
35
|
packet 을 finalize 하기 전에 문제 의존을 끊어 DAG 로 만든다.
|
|
36
|
+
- brief 의 `Related Task Graph` 에서 seed 된 ordering edge 는 각 packet 의 `depends-on`
|
|
37
|
+
과 final report 의 routing 근거에 보존한다. `duplicates` / `related-to` edge 는
|
|
38
|
+
fan-out 순서로 해석하지 말고 중복 작업 방지 / scope alignment 참고로만 사용한다.
|
|
35
39
|
- 최종 리포트에는 분해 결과를 중복하지 말고 "fan-out: N packets → fan-out/index.md" 한 줄
|
|
36
40
|
포인터만 둔다. packet 실행은 별도다: 사용자가 `okstra-run --task-brief <packet 경로>` 로
|
|
37
41
|
각 단위를 새 task-key 로 시작한다(이 phase 는 다운스트림 run 을 직접 시작하지 않는다).
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""CLI entrypoint for okstra manager."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from okstra_project.dirs import okstra_home
|
|
11
|
+
|
|
12
|
+
from .manager_launch import build_launch_packet
|
|
13
|
+
from .manager_store import (
|
|
14
|
+
ManagerError,
|
|
15
|
+
append_directive,
|
|
16
|
+
assign_child,
|
|
17
|
+
create_task,
|
|
18
|
+
create_task_group,
|
|
19
|
+
discover_projects,
|
|
20
|
+
init_manager,
|
|
21
|
+
link_project,
|
|
22
|
+
)
|
|
23
|
+
from .manager_sync import status_task, sync_task
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _emit(payload: dict | list[dict], as_json: bool) -> None:
|
|
27
|
+
if as_json:
|
|
28
|
+
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
29
|
+
return
|
|
30
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _normalize_global_flags(argv: list[str] | None) -> list[str]:
|
|
34
|
+
values = list(argv or [])
|
|
35
|
+
out: list[str] = []
|
|
36
|
+
json_seen = False
|
|
37
|
+
for value in values:
|
|
38
|
+
if value == "--json":
|
|
39
|
+
json_seen = True
|
|
40
|
+
continue
|
|
41
|
+
out.append(value)
|
|
42
|
+
return (["--json"] if json_seen else []) + out
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _parse_child_specs(values: list[str], task_group: str, default_task_id: str) -> list[dict]:
|
|
46
|
+
specs: list[dict] = []
|
|
47
|
+
for value in values:
|
|
48
|
+
parts = value.split(":")
|
|
49
|
+
if len(parts) == 1:
|
|
50
|
+
specs.append({"projectId": parts[0], "taskId": default_task_id})
|
|
51
|
+
continue
|
|
52
|
+
if len(parts) == 2:
|
|
53
|
+
specs.append({"projectId": parts[0], "taskId": parts[1]})
|
|
54
|
+
continue
|
|
55
|
+
if len(parts) == 3:
|
|
56
|
+
project_id, embedded_task_group, child_task_id = parts
|
|
57
|
+
if embedded_task_group != task_group:
|
|
58
|
+
raise ManagerError(
|
|
59
|
+
f"task-group mismatch for --task {value!r}: expected {task_group!r}, got {embedded_task_group!r}"
|
|
60
|
+
)
|
|
61
|
+
specs.append({"projectId": project_id, "taskId": child_task_id})
|
|
62
|
+
continue
|
|
63
|
+
raise ManagerError(
|
|
64
|
+
f"invalid --task {value!r}: expected project-id, project-id:task-id, or project-id:task-group:task-id"
|
|
65
|
+
)
|
|
66
|
+
return specs
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parser() -> argparse.ArgumentParser:
|
|
70
|
+
parser = argparse.ArgumentParser(prog="okstra manager")
|
|
71
|
+
parser.add_argument("--json", action="store_true")
|
|
72
|
+
parser.add_argument("--workspace-root", default="")
|
|
73
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
74
|
+
|
|
75
|
+
init = sub.add_parser("init")
|
|
76
|
+
init.add_argument("--manager-id", required=True)
|
|
77
|
+
|
|
78
|
+
sub.add_parser("discover-projects")
|
|
79
|
+
|
|
80
|
+
new = sub.add_parser("new")
|
|
81
|
+
new_sub = new.add_subparsers(dest="new_command", required=True)
|
|
82
|
+
|
|
83
|
+
project = new_sub.add_parser("project")
|
|
84
|
+
project.add_argument("--manager-id", required=True)
|
|
85
|
+
project.add_argument("--project-id", required=True)
|
|
86
|
+
project.add_argument("--project-root", required=True)
|
|
87
|
+
project.add_argument("--role", default="")
|
|
88
|
+
project.add_argument("--tag", action="append", default=[])
|
|
89
|
+
|
|
90
|
+
group = new_sub.add_parser("task-group")
|
|
91
|
+
group.add_argument("--manager-id", required=True)
|
|
92
|
+
group.add_argument("--task-group", required=True)
|
|
93
|
+
|
|
94
|
+
task_new = new_sub.add_parser("task")
|
|
95
|
+
task_new.add_argument("--manager-id", required=True)
|
|
96
|
+
task_new.add_argument("--task-group", required=True)
|
|
97
|
+
task_new.add_argument("--task-id", required=True)
|
|
98
|
+
task_new.add_argument("--task", action="append", default=[])
|
|
99
|
+
task_new.add_argument("--objective", default="")
|
|
100
|
+
task_new.add_argument("--common-brief", default="")
|
|
101
|
+
task_new.add_argument("--progress-mode", choices=["manual", "auto"], default="manual")
|
|
102
|
+
|
|
103
|
+
task = sub.add_parser("task")
|
|
104
|
+
task_sub = task.add_subparsers(dest="task_command", required=True)
|
|
105
|
+
|
|
106
|
+
for name in ("status", "sync"):
|
|
107
|
+
command = task_sub.add_parser(name)
|
|
108
|
+
command.add_argument("--manager-id", required=True)
|
|
109
|
+
command.add_argument("--task-group", required=True)
|
|
110
|
+
command.add_argument("--task-id", required=True)
|
|
111
|
+
|
|
112
|
+
assign = task_sub.add_parser("assign")
|
|
113
|
+
assign.add_argument("--manager-id", required=True)
|
|
114
|
+
assign.add_argument("--task-group", required=True)
|
|
115
|
+
assign.add_argument("--task-id", required=True)
|
|
116
|
+
assign.add_argument("--project-id", required=True)
|
|
117
|
+
assign.add_argument("--child-task-id", default=None)
|
|
118
|
+
assign.add_argument("--role", default="")
|
|
119
|
+
assign.add_argument("--tag", action="append", default=[])
|
|
120
|
+
assign.add_argument("--assignment", default="")
|
|
121
|
+
|
|
122
|
+
note = task_sub.add_parser("note")
|
|
123
|
+
note.add_argument("--manager-id", required=True)
|
|
124
|
+
note.add_argument("--task-group", required=True)
|
|
125
|
+
note.add_argument("--task-id", required=True)
|
|
126
|
+
note.add_argument("--scope", choices=["shared", "project"], required=True)
|
|
127
|
+
note.add_argument("--project-id", default="")
|
|
128
|
+
note.add_argument("--body", required=True)
|
|
129
|
+
|
|
130
|
+
run = task_sub.add_parser("run")
|
|
131
|
+
run.add_argument("--manager-id", required=True)
|
|
132
|
+
run.add_argument("--project-id", required=True)
|
|
133
|
+
run.add_argument("--task-group", required=True)
|
|
134
|
+
run.add_argument("--task-id", required=True)
|
|
135
|
+
run.add_argument("--child-task-id", default=None)
|
|
136
|
+
return parser
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main(argv: list[str] | None = None) -> int:
|
|
140
|
+
parser = _parser()
|
|
141
|
+
args = parser.parse_args(_normalize_global_flags(argv if argv is not None else sys.argv[1:]))
|
|
142
|
+
home = okstra_home()
|
|
143
|
+
try:
|
|
144
|
+
if args.command == "init":
|
|
145
|
+
_emit(init_manager(home, args.manager_id), args.json)
|
|
146
|
+
elif args.command == "discover-projects":
|
|
147
|
+
_emit(discover_projects(home), args.json)
|
|
148
|
+
elif args.command == "new" and args.new_command == "project":
|
|
149
|
+
_emit(
|
|
150
|
+
link_project(
|
|
151
|
+
home,
|
|
152
|
+
args.manager_id,
|
|
153
|
+
args.project_id,
|
|
154
|
+
Path(args.project_root),
|
|
155
|
+
role=args.role,
|
|
156
|
+
tags=args.tag,
|
|
157
|
+
),
|
|
158
|
+
args.json,
|
|
159
|
+
)
|
|
160
|
+
elif args.command == "new" and args.new_command == "task-group":
|
|
161
|
+
_emit(create_task_group(home, args.manager_id, args.task_group), args.json)
|
|
162
|
+
elif args.command == "new" and args.new_command == "task":
|
|
163
|
+
_emit(
|
|
164
|
+
create_task(
|
|
165
|
+
home,
|
|
166
|
+
args.manager_id,
|
|
167
|
+
args.task_group,
|
|
168
|
+
args.task_id,
|
|
169
|
+
_parse_child_specs(args.task, args.task_group, args.task_id),
|
|
170
|
+
objective=args.objective,
|
|
171
|
+
common_brief_path=args.common_brief,
|
|
172
|
+
progress_mode=args.progress_mode,
|
|
173
|
+
),
|
|
174
|
+
args.json,
|
|
175
|
+
)
|
|
176
|
+
elif args.command == "task" and args.task_command == "assign":
|
|
177
|
+
_emit(
|
|
178
|
+
assign_child(
|
|
179
|
+
home,
|
|
180
|
+
args.manager_id,
|
|
181
|
+
args.task_group,
|
|
182
|
+
args.task_id,
|
|
183
|
+
args.project_id,
|
|
184
|
+
role=args.role,
|
|
185
|
+
tags=args.tag,
|
|
186
|
+
assignment=args.assignment,
|
|
187
|
+
child_task_id=args.child_task_id,
|
|
188
|
+
),
|
|
189
|
+
args.json,
|
|
190
|
+
)
|
|
191
|
+
elif args.command == "task" and args.task_command == "note":
|
|
192
|
+
_emit(
|
|
193
|
+
append_directive(
|
|
194
|
+
home,
|
|
195
|
+
args.manager_id,
|
|
196
|
+
args.task_group,
|
|
197
|
+
args.task_id,
|
|
198
|
+
scope=args.scope,
|
|
199
|
+
project_id=args.project_id,
|
|
200
|
+
body=args.body,
|
|
201
|
+
),
|
|
202
|
+
args.json,
|
|
203
|
+
)
|
|
204
|
+
elif args.command == "task" and args.task_command == "sync":
|
|
205
|
+
_emit(sync_task(home, args.manager_id, args.task_group, args.task_id), args.json)
|
|
206
|
+
elif args.command == "task" and args.task_command == "status":
|
|
207
|
+
_emit(status_task(home, args.manager_id, args.task_group, args.task_id), args.json)
|
|
208
|
+
elif args.command == "task" and args.task_command == "run":
|
|
209
|
+
_emit(
|
|
210
|
+
build_launch_packet(
|
|
211
|
+
home,
|
|
212
|
+
args.manager_id,
|
|
213
|
+
args.task_group,
|
|
214
|
+
args.task_id,
|
|
215
|
+
args.project_id,
|
|
216
|
+
child_task_id=args.child_task_id,
|
|
217
|
+
env=os.environ,
|
|
218
|
+
),
|
|
219
|
+
args.json,
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
parser.print_help()
|
|
223
|
+
return 2
|
|
224
|
+
return 0
|
|
225
|
+
except ManagerError as exc:
|
|
226
|
+
print(f"okstra manager: {exc}", file=sys.stderr)
|
|
227
|
+
return 2
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
raise SystemExit(main(sys.argv[1:]))
|