makdoong2-team 2.3.2 → 3.0.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.md +4 -12
- package/agents/makdoong2-engineer.md +1 -0
- package/agents/makdoong2-planner.md +17 -61
- package/agents/makdoong2-team-leader.md +14 -4
- package/agents/makdoong2-verifier.md +6 -6
- package/assets/makdoong2-team.schema.json +0 -18
- package/bin/cli.js +27 -1
- package/bin/cli.ts +27 -1
- package/dist/agent-stage-config.d.ts +1 -1
- package/dist/agent-stage-config.js +0 -11
- package/dist/apply-patch-paths.d.ts +46 -0
- package/dist/apply-patch-paths.js +114 -0
- package/dist/config.d.ts +22 -9
- package/dist/config.js +61 -11
- package/dist/model-fallback-policy.js +0 -4
- package/dist/opencode-plugin.js +270 -356
- package/dist/poll-sub-session.d.ts +16 -1
- package/dist/poll-sub-session.js +76 -11
- package/dist/sub-session-registry.d.ts +74 -0
- package/dist/sub-session-registry.js +204 -0
- package/gates/stage-analysis-verify.sh +63 -5
- package/gates/stage4-dev-verify.sh +6 -6
- package/gates/verify.sh +0 -2
- package/opencode.json.example +0 -1
- package/package.json +1 -1
- package/scripts/gate-policy-test.sh +15 -14
- package/scripts/install-lib.mjs +1 -1
- package/scripts/install-lib.mts +1 -1
- package/scripts/model-policy.mjs +0 -4
- package/scripts/model-policy.mts +0 -4
- package/scripts/release.sh +13 -1
- package/scripts/run-tests.mts +4 -1
- package/scripts/state.sh +24 -2
- package/skills/_lib/load-secret.sh +15 -5
- package/src/hooks/session-start.sh +0 -1
- package/stages/01-planning.md +35 -54
- package/stages/02-requirements.md +53 -35
- package/stages/04-analysis.md +2 -2
- package/stages/05-worktree-dev.md +15 -0
- package/agents/makdoong2-researcher.md +0 -68
- package/dist/research-fanout.d.ts +0 -165
- package/dist/research-fanout.js +0 -341
- package/gates/stage3-scope-verify.sh +0 -69
- package/stages/03-scope.md +0 -81
|
@@ -51,44 +51,45 @@ expect(){
|
|
|
51
51
|
echo ""
|
|
52
52
|
echo "[gate-policy-test] 조건부 auto-approve 게이트 분기"
|
|
53
53
|
echo ""
|
|
54
|
-
echo "──
|
|
54
|
+
echo "── stage-analysis-verify (requirements→analysis) ──"
|
|
55
|
+
# scope substage 흡수로 stage-analysis-verify.sh 의 승인·정합성 검사가 이 게이트로 이관됐다.
|
|
55
56
|
rm -f "$REPORT"
|
|
56
57
|
|
|
57
58
|
# minor (기본): auto_approve true → 승인/pending 무시하고 통과
|
|
58
59
|
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".verification_pending=true | .policy={category:"minor",auto_approve:{"1_planning.requirements":true}}'
|
|
59
|
-
expect "minor 기본 auto-approve (승인 없이 통과)" pass
|
|
60
|
+
expect "minor 기본 auto-approve (승인 없이 통과)" pass stage-analysis-verify.sh
|
|
60
61
|
|
|
61
62
|
# major (기본): auto_approve true → 통과 (minor 와 동일 흐름 — category 는 라벨용)
|
|
62
63
|
mk '.stages."1_planning".substages."requirements".done=true | .policy={category:"major",auto_approve:{"1_planning.requirements":true}}'
|
|
63
|
-
expect "major 기본도 요구사항 게이트는 무인 통과" pass
|
|
64
|
+
expect "major 기본도 요구사항 게이트는 무인 통과" pass stage-analysis-verify.sh
|
|
64
65
|
|
|
65
66
|
# legacy(no policy) + 미승인 → 차단
|
|
66
67
|
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".approved_by_user=false'
|
|
67
|
-
expect "구형 state 미승인 → 차단" block
|
|
68
|
+
expect "구형 state 미승인 → 차단" block stage-analysis-verify.sh
|
|
68
69
|
|
|
69
70
|
# legacy(no policy) + 승인 → 통과
|
|
70
71
|
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".approved_by_user=true | .stages."1_planning".substages."requirements".verification_pending=false'
|
|
71
|
-
expect "구형 state 사용자 승인 → 통과" pass
|
|
72
|
+
expect "구형 state 사용자 승인 → 통과" pass stage-analysis-verify.sh
|
|
72
73
|
|
|
73
74
|
# auto-approve여도 인터뷰 미완료면 차단 (정합성 게이트는 독립)
|
|
74
75
|
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".interview_required=true | .stages."1_planning".substages."requirements".interview_completed=false | .policy={category:"minor",auto_approve:{"1_planning.requirements":true}}'
|
|
75
|
-
expect "인터뷰 미완료 → 차단 (auto와 무관)" block
|
|
76
|
+
expect "인터뷰 미완료 → 차단 (auto와 무관)" block stage-analysis-verify.sh
|
|
76
77
|
|
|
77
78
|
echo ""
|
|
78
79
|
echo "── stage4-dev-verify (3→4) ──"
|
|
79
80
|
# worktree 경로 검증을 위해 실제 git worktree 생성 (메인 repo의 형제)
|
|
80
81
|
WT="$(dirname "$TMP")/$(basename "$TMP")-TEST-1"
|
|
81
82
|
(cd "$ROOT" && git worktree add "$WT" -b test-branch 2>/dev/null || true)
|
|
82
|
-
mk '.stages."1_planning".substages."
|
|
83
|
+
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".verification_pending=true | .stages."2_implementation".substages."analysis".done=true | .policy={category:"minor",auto_approve:{"1_planning.requirements":true}} | .worktree="'"$WT"'"'
|
|
83
84
|
expect "minor auto-approve → 통과" pass stage4-dev-verify.sh
|
|
84
|
-
mk '.stages."1_planning".substages."
|
|
85
|
+
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".approved_by_user=false | .stages."2_implementation".substages."analysis".done=true | .worktree="'"$WT"'"'
|
|
85
86
|
expect "구형 state 미승인 → 차단" block stage4-dev-verify.sh
|
|
86
|
-
mk '.stages."1_planning".substages."
|
|
87
|
+
mk '.stages."1_planning".substages."requirements".done=true | .stages."1_planning".substages."requirements".approved_by_user=true | .stages."2_implementation".substages."analysis".done=true | .worktree="'"$WT"'"'
|
|
87
88
|
expect "구형 state 승인 → 통과" pass stage4-dev-verify.sh
|
|
88
89
|
|
|
89
90
|
echo ""
|
|
90
91
|
echo "── stage6-commit-verify (5→6) ──"
|
|
91
|
-
TESTS='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."
|
|
92
|
+
TESTS='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."2_implementation".substages."dev".done=true | .stages."2_implementation".substages."test".unit="pass" | .stages."2_implementation".substages."test".integration="skip" | .stages."2_implementation".substages."test".coverage="exempt"'
|
|
92
93
|
|
|
93
94
|
# minor (기본): commit auto_approve true → 보고서/승인 없이 통과
|
|
94
95
|
rm -f "$REPORT"
|
|
@@ -130,20 +131,20 @@ expect "구형 state → 휴먼 게이트 없이 통과" pass stage6-commit-veri
|
|
|
130
131
|
|
|
131
132
|
echo ""
|
|
132
133
|
echo "── stage8-review-verify (7→8) ──"
|
|
133
|
-
BV='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."
|
|
134
|
+
BV='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."2_implementation".substages."dev".done=true | .stages."2_implementation".substages."test".unit="pass" | .stages."3_delivery".substages."commit".done=true | .stages."3_delivery".substages."pr".draft_url="http://pr/1" | .stages."3_delivery".substages."pr".body_validation={no_orphan_scenarios:true,template_match:true,section_content_match:true} | .stages."3_delivery".substages."pr".reviewer_added=true | .stages."3_delivery".substages."review".comments=1 | .stages."3_delivery".substages."review".all_comments_inline=true'
|
|
134
135
|
mk "$BV | .policy={category:\"minor\",auto_approve:{\"3_delivery.pr\":true}}"
|
|
135
136
|
expect "minor auto-approve → 통과" pass stage8-review-verify.sh
|
|
136
137
|
mk "$BV | .stages.\"3_delivery\".substages.\"pr\".approved_by_user=false"
|
|
137
138
|
expect "구형 state 미승인 → 차단" block stage8-review-verify.sh
|
|
138
139
|
mk "$BV | .stages.\"3_delivery\".substages.\"pr\".approved_by_user=true | .stages.\"3_delivery\".substages.\"pr\".verification_pending=false"
|
|
139
140
|
expect "구형 state 승인 → 통과" pass stage8-review-verify.sh
|
|
140
|
-
mk '.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."
|
|
141
|
+
mk '.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."2_implementation".substages."dev".done=true | .stages."2_implementation".substages."test".unit="pass" | .stages."3_delivery".substages."commit".done=true | .stages."3_delivery".substages."pr".draft_url="http://pr/1" | .stages."3_delivery".substages."pr".body_validation={no_orphan_scenarios:false,template_match:true,section_content_match:true} | .stages."3_delivery".substages."pr".reviewer_added=true | .stages."3_delivery".substages."review".comments=1 | .stages."3_delivery".substages."review".all_comments_inline=true | .policy={category:"minor",auto_approve:{"3_delivery.pr":true}}'
|
|
141
142
|
expect "body_validation false → 차단 (auto와 무관)" block stage8-review-verify.sh
|
|
142
143
|
|
|
143
|
-
BSS='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."
|
|
144
|
+
BSS='.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."2_implementation".substages."dev".done=true | .stages."2_implementation".substages."test".unit="pass" | .stages."3_delivery".substages."commit".done=true | .stages."3_delivery".substages."pr".draft_url="http://pr/1" | .stages."3_delivery".substages."pr".body_validation={no_orphan_scenarios:true,template_match:true,section_content_match:true} | .stages."3_delivery".substages."pr".reviewer_self_skipped=true | .stages."3_delivery".substages."review".comments=1 | .stages."3_delivery".substages."review".all_comments_inline=true'
|
|
144
145
|
mk "$BSS | .policy={category:\"minor\",auto_approve:{\"3_delivery.pr\":true}}"
|
|
145
146
|
expect "reviewer_self_skipped=true → reviewer 없어도 통과" pass stage8-review-verify.sh
|
|
146
|
-
mk '.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."
|
|
147
|
+
mk '.stages."1_planning".substages."jira".done=true | .stages."1_planning".substages."requirements".done=true | .stages."2_implementation".substages."dev".done=true | .stages."2_implementation".substages."test".unit="pass" | .stages."3_delivery".substages."commit".done=true | .stages."3_delivery".substages."pr".draft_url="http://pr/1" | .stages."3_delivery".substages."pr".body_validation={no_orphan_scenarios:true,template_match:true,section_content_match:true} | .stages."3_delivery".substages."review".comments=1 | .stages."3_delivery".substages."review".all_comments_inline=true | .policy={category:"minor",auto_approve:{"3_delivery.pr":true}}'
|
|
147
148
|
expect "reviewer_added·reviewer_self_skipped 모두 없음 → 차단" block stage8-review-verify.sh
|
|
148
149
|
|
|
149
150
|
echo ""
|
package/scripts/install-lib.mjs
CHANGED
|
@@ -134,7 +134,7 @@ const LEGACY_RELATIVE_PLUGIN_REF = "./plugins/makdoong2-team/src/opencode-plugin
|
|
|
134
134
|
const LEGACY_ABSOLUTE_TS_SUFFIX = "/src/opencode-plugin.ts";
|
|
135
135
|
const LEGACY_SCOPED_NAME = "@local/makdoong2-team";
|
|
136
136
|
const PLUGIN_PACKAGE_NAME = "makdoong2-team";
|
|
137
|
-
const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "
|
|
137
|
+
const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "auto_advance_stage", "get_fallback_model"];
|
|
138
138
|
const TOOL_SEARCH_PLUGIN_PREFIX = "opencode-tool-search";
|
|
139
139
|
// UTIL_SCRIPTS is no longer deployed (scripts live inside the npm module).
|
|
140
140
|
// Retained for cleanup: older installs copied these into ~/.config/opencode/scripts/
|
package/scripts/install-lib.mts
CHANGED
|
@@ -164,7 +164,7 @@ const LEGACY_RELATIVE_PLUGIN_REF = "./plugins/makdoong2-team/src/opencode-plugin
|
|
|
164
164
|
const LEGACY_ABSOLUTE_TS_SUFFIX = "/src/opencode-plugin.ts";
|
|
165
165
|
const LEGACY_SCOPED_NAME = "@local/makdoong2-team";
|
|
166
166
|
const PLUGIN_PACKAGE_NAME = "makdoong2-team";
|
|
167
|
-
const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "
|
|
167
|
+
const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "auto_advance_stage", "get_fallback_model"];
|
|
168
168
|
const TOOL_SEARCH_PLUGIN_PREFIX = "opencode-tool-search";
|
|
169
169
|
// UTIL_SCRIPTS is no longer deployed (scripts live inside the npm module).
|
|
170
170
|
// Retained for cleanup: older installs copied these into ~/.config/opencode/scripts/
|
package/scripts/model-policy.mjs
CHANGED
|
@@ -69,10 +69,6 @@ export const DEFAULT_POLICIES = Object.freeze({
|
|
|
69
69
|
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
|
70
70
|
},
|
|
71
71
|
// Research fan-out worker — one session per source, spawned in parallel.
|
|
72
|
-
"makdoong2-researcher": {
|
|
73
|
-
primary: { id: "github-copilot/gpt-5.6-luna", variant: "xhigh", tier: "medium" },
|
|
74
|
-
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
|
75
|
-
},
|
|
76
72
|
"makdoong2-planner": {
|
|
77
73
|
primary: { id: "github-copilot/gpt-5.6-luna", variant: "xhigh", tier: "medium" },
|
|
78
74
|
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
package/scripts/model-policy.mts
CHANGED
|
@@ -130,10 +130,6 @@ export const DEFAULT_POLICIES: Readonly<Record<string, AgentModelPolicy>> = Obje
|
|
|
130
130
|
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
|
131
131
|
},
|
|
132
132
|
// Research fan-out worker — one session per source, spawned in parallel.
|
|
133
|
-
"makdoong2-researcher": {
|
|
134
|
-
primary: { id: "github-copilot/gpt-5.6-luna", variant: "xhigh", tier: "medium" },
|
|
135
|
-
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
|
136
|
-
},
|
|
137
133
|
"makdoong2-planner": {
|
|
138
134
|
primary: { id: "github-copilot/gpt-5.6-luna", variant: "xhigh", tier: "medium" },
|
|
139
135
|
fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
|
package/scripts/release.sh
CHANGED
|
@@ -160,7 +160,19 @@ info "다음 버전: $NEXT_VERSION"
|
|
|
160
160
|
log "이번 릴리스에 포함될 커밋:"
|
|
161
161
|
LAST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || echo '')"
|
|
162
162
|
if [ -n "$LAST_TAG" ]; then
|
|
163
|
-
git log
|
|
163
|
+
# `git log … | head -N` 을 쓰지 않는다. set -o pipefail 아래서 head 가 N 줄을
|
|
164
|
+
# 읽고 먼저 종료하면 git 이 SIGPIPE(141)로 죽고, 그 코드가 파이프라인 전체의
|
|
165
|
+
# 상태가 되어 errexit 가 **릴리스를 통째로 중단**시킨다. 커밋이 20개를 넘은
|
|
166
|
+
# 적이 없어 지금까지 잠복해 있었고, 48개 릴리스에서 실제로 발화했다.
|
|
167
|
+
# "1 파일 = 1 commit" 규약을 지키면 커밋 수는 앞으로도 쉽게 20을 넘는다.
|
|
168
|
+
# 개수 제한은 git 에게 맡긴다 — 파이프가 없어지면 발화 조건 자체가 사라진다.
|
|
169
|
+
COMMIT_TOTAL="$(git rev-list --count "${LAST_TAG}..HEAD")"
|
|
170
|
+
git log --oneline -20 "${LAST_TAG}..HEAD"
|
|
171
|
+
# `[ … ] && info …` 형태로 쓰지 않는다. 조건이 거짓이면 AND 리스트 전체가 1을
|
|
172
|
+
# 반환하고 errexit 가 그 자리에서 스크립트를 끝낸다 (CLAUDE.md 게이트 사례와 동형).
|
|
173
|
+
if [ "$COMMIT_TOTAL" -gt 20 ]; then
|
|
174
|
+
info "... 외 $((COMMIT_TOTAL - 20))개 (총 ${COMMIT_TOTAL}개)"
|
|
175
|
+
fi
|
|
164
176
|
else
|
|
165
177
|
git log --oneline -10
|
|
166
178
|
fi
|
package/scripts/run-tests.mts
CHANGED
|
@@ -41,6 +41,9 @@ const STEPS = [
|
|
|
41
41
|
"node test/install-lib.test.ts",
|
|
42
42
|
"node test/skill-mcp-registry.test.ts",
|
|
43
43
|
"node --test test/state-write-guard.test.ts",
|
|
44
|
+
"node --test test/apply-patch-paths.test.ts",
|
|
45
|
+
"node --test test/pipeline-shape.test.ts",
|
|
46
|
+
"node --test test/permission-scope-hardening.test.ts",
|
|
44
47
|
"node --test test/state-access-guard.test.ts",
|
|
45
48
|
"node --test test/git-exclude-registration.test.ts",
|
|
46
49
|
"node --test test/issue-reporter-guard.test.ts",
|
|
@@ -73,6 +76,7 @@ const STEPS = [
|
|
|
73
76
|
"node --test test/verdict-reason-injection.test.ts",
|
|
74
77
|
"node --test test/verdict-hash-normalize.test.ts",
|
|
75
78
|
"node --test test/session-index-fallback.test.ts",
|
|
79
|
+
"node --test test/sub-session-registry.test.ts",
|
|
76
80
|
"node --test test/plugin-exports-shape.test.ts",
|
|
77
81
|
"node --test test/stale-worktree-recovery.test.ts",
|
|
78
82
|
"node --test test/rollback-commits.test.ts",
|
|
@@ -82,7 +86,6 @@ const STEPS = [
|
|
|
82
86
|
"node --test test/with-fallback-no-bun.test.ts",
|
|
83
87
|
"node --test test/doctor-exit-code.test.ts",
|
|
84
88
|
"node --test test/example-config-portability.test.ts",
|
|
85
|
-
"node --test test/research-fanout.test.ts",
|
|
86
89
|
"node --test test/stage-completion.test.ts",
|
|
87
90
|
"node --test test/state-sh-write-atomicity.test.ts",
|
|
88
91
|
"node --test test/gate-locale-and-path.test.ts",
|
package/scripts/state.sh
CHANGED
|
@@ -185,8 +185,7 @@ case "$cmd" in
|
|
|
185
185
|
"done": false,
|
|
186
186
|
"substages": {
|
|
187
187
|
"jira": {"done": false},
|
|
188
|
-
"requirements": {"done": false, "approved_by_user": false}
|
|
189
|
-
"scope": {"done": false, "approved_by_user": false}
|
|
188
|
+
"requirements": {"done": false, "approved_by_user": false}
|
|
190
189
|
}
|
|
191
190
|
},
|
|
192
191
|
"2_implementation": {
|
|
@@ -333,6 +332,29 @@ JSON
|
|
|
333
332
|
| move("3_delivery.commit"; "3_delivery"; "commit")
|
|
334
333
|
| move("3_delivery.pr"; "3_delivery"; "pr")
|
|
335
334
|
| move("3_delivery.review"; "3_delivery"; "review")
|
|
335
|
+
|
|
336
|
+
# scope substage 흡수 (범위 확정 → requirements).
|
|
337
|
+
# 진행 중이던 워크플로우가 남긴 scope 마커를 버리지 않고 requirements 로
|
|
338
|
+
# 접는다. done / approved_by_user 는 **AND** 로 접는다 — scope 가 아직
|
|
339
|
+
# 끝나지 않았는데 requirements 만 보고 통과시키면, 흡수 전이라면 막혔을
|
|
340
|
+
# 지점을 마이그레이션이 열어주는 셈이 된다. 접은 뒤 scope 키는 지운다.
|
|
341
|
+
| (if (.stages."1_planning".substages? // {} | has("scope")) then
|
|
342
|
+
.stages."1_planning".substages.requirements =
|
|
343
|
+
((.stages."1_planning".substages.requirements // {})
|
|
344
|
+
+ {"done": ((.stages."1_planning".substages.requirements.done // false)
|
|
345
|
+
and (.stages."1_planning".substages.scope.done // false)),
|
|
346
|
+
"approved_by_user":
|
|
347
|
+
((.stages."1_planning".substages.requirements.approved_by_user // false)
|
|
348
|
+
and (.stages."1_planning".substages.scope.approved_by_user // false))})
|
|
349
|
+
| del(.stages."1_planning".substages.scope)
|
|
350
|
+
else . end)
|
|
351
|
+
# auto_approve 는 flat 표기를 유지하는 유일한 예외라 여기서 같이 옮긴다.
|
|
352
|
+
| (if (.policy.auto_approve? // {} | has("1_planning.scope")) then
|
|
353
|
+
.policy.auto_approve = (.policy.auto_approve
|
|
354
|
+
| ."1_planning.requirements" =
|
|
355
|
+
((."1_planning.requirements" // false) and ."1_planning.scope")
|
|
356
|
+
| del(."1_planning.scope"))
|
|
357
|
+
else . end)
|
|
336
358
|
'
|
|
337
359
|
echo "migrated[$ISSUE] $P" ;;
|
|
338
360
|
*) echo "usage: state.sh {root|issue|init|status|get|set|append|migrate} ..." >&2; exit 64 ;;
|
|
@@ -42,16 +42,26 @@ EOF
|
|
|
42
42
|
|
|
43
43
|
# `jq -e` fails when the file cannot be parsed; capture stderr for diagnostics.
|
|
44
44
|
local token jq_err
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
# 임시 파일은 **설정 파일과 같은 디렉토리**에 만든다. 종전 폴백은 `/tmp` 을
|
|
46
|
+
# 명령문에 노출시켰는데, opencode 1.18 의 ShellTool 은 bash 명령이 참조하는
|
|
47
|
+
# 디렉토리마다 external_directory 승인을 묻고, 서브에이전트 세션에서 그 요청은
|
|
48
|
+
# 자동 거부 + 세션 abort 로 이어진다 (워크스페이스 밖 경로이므로).
|
|
49
|
+
# mktemp 실패 시에도 워크스페이스 밖으로 새지 않게 한다.
|
|
50
|
+
jq_err=""
|
|
51
|
+
jq_err=$(mktemp "$(dirname "$cfg_file")/.load-secret.XXXXXX" 2>/dev/null) || jq_err=""
|
|
52
|
+
# mktemp 실패 시 /dev/null 로 흘린다. 이때 `jq_err` 는 빈 문자열로 남으므로
|
|
53
|
+
# 아래 정리 단계가 **실제로 만든 파일만** 지운다 — `rm -f /dev/null` 은
|
|
54
|
+
# root 로 도는 환경(이슈 #8 로그의 /root/.nvm)에서 진짜로 장치 노드를 지운다.
|
|
55
|
+
local jq_err_sink="${jq_err:-/dev/null}"
|
|
56
|
+
token=$(jq -r --arg k "$var_name" '.secrets[$k] // empty' "$cfg_file" 2>"$jq_err_sink")
|
|
47
57
|
local jq_status=$?
|
|
48
58
|
if [ "$jq_status" -ne 0 ]; then
|
|
49
59
|
printf '[%s] makdoong2-team.json is not valid JSON: %s\n' "$tag" "$cfg_file" >&2
|
|
50
|
-
if [ -s "$
|
|
51
|
-
rm -f "$jq_err"
|
|
60
|
+
if [ -s "$jq_err_sink" ]; then sed -e "s/^/[$tag] jq: /" "$jq_err_sink" >&2; fi
|
|
61
|
+
if [ -n "$jq_err" ]; then rm -f -- "$jq_err"; fi
|
|
52
62
|
return 67
|
|
53
63
|
fi
|
|
54
|
-
rm -f "$jq_err"
|
|
64
|
+
if [ -n "$jq_err" ]; then rm -f -- "$jq_err"; fi
|
|
55
65
|
|
|
56
66
|
if [ -z "$token" ] || [ "$token" = "null" ]; then
|
|
57
67
|
cat >&2 <<EOF
|
|
@@ -55,7 +55,6 @@ jq -r '
|
|
|
55
55
|
# Planning substages
|
|
56
56
|
($p1.substages.jira // {} | " 1_planning.jira: done=" + ((.done // false) | tostring) + (if .approved_by_user then " (approved)" else "" end)),
|
|
57
57
|
($p1.substages.requirements // {} | " 1_planning.requirements: done=" + ((.done // false) | tostring) + (if .approved_by_user then " (approved)" else "" end)),
|
|
58
|
-
($p1.substages.scope // {} | " 1_planning.scope: done=" + ((.done // false) | tostring) + (if .approved_by_user then " (approved)" else "" end)),
|
|
59
58
|
|
|
60
59
|
# Implementation substages
|
|
61
60
|
($p2.substages.analysis // {} | " 2_implementation.analysis: done=" + ((.done // false) | tostring) + (if .skipped then " (skipped)" else "" end)),
|
package/stages/01-planning.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 통합 Planning — `1_planning.{jira,requirements}` 연속 처리
|
|
2
2
|
|
|
3
3
|
**목적**: Jira 조회 → 요구사항 확정 → 개발 범위 파악을 **단일 세션**에서 완료한다.
|
|
4
4
|
**속도**: 3개 substage dispatch(모델 호출 3회)를 1회로 단축.
|
|
@@ -15,12 +15,10 @@
|
|
|
15
15
|
```bash
|
|
16
16
|
bash <SCRIPTS_DIR>/state.sh get <이슈키> '.stages."1_planning".substages."jira".done'
|
|
17
17
|
bash <SCRIPTS_DIR>/state.sh get <이슈키> '.stages."1_planning".substages."requirements".done'
|
|
18
|
-
bash <SCRIPTS_DIR>/state.sh get <이슈키> '.stages."1_planning".substages."scope".done'
|
|
19
18
|
```
|
|
20
19
|
|
|
21
20
|
- `jira.done=true` → Phase 1 건너뛰고 Phase 2로 직행
|
|
22
|
-
-
|
|
23
|
-
- 셋 다 `true` → 모두 완료. 완료 메시지 출력 후 종료
|
|
21
|
+
- 둘 다 `true` → 모두 완료. 완료 메시지 출력 후 종료
|
|
24
22
|
|
|
25
23
|
---
|
|
26
24
|
|
|
@@ -89,7 +87,11 @@ mkdir -p .makdoong2-team/<이슈키>
|
|
|
89
87
|
|
|
90
88
|
파일 (repo/worktree root 기준 상대경로): `.makdoong2-team/<이슈키>/requirements-draft.md`
|
|
91
89
|
|
|
92
|
-
**초안 파일은 반드시
|
|
90
|
+
**초안 파일은 반드시 쓰기 *툴* 로 생성·갱신한다 — bash 리디렉션(`cat > …`, `printf > …`, `tee`, `sed -i` 등)은 같은 경로라도 훅이 차단한다.**
|
|
91
|
+
- 세션에 `write` 툴이 있으면 `write(filePath=…)` 를 쓴다.
|
|
92
|
+
- **`write` 가 툴 목록에 없으면 `apply_patch` 를 쓴다.** opencode 는 모델에 따라 쓰기 툴을 바꿔 끼운다 — `gpt-5` 계열 세션에는 `write`·`edit` 가 아예 없고 `apply_patch` 만 있다. 이때 패치 본문은 `*** Begin Patch` / `*** Add File: .makdoong2-team/<이슈키>/requirements-draft.md` / `*** End Patch` 형식으로 정확히 작성한다 — 훅이 본문에서 대상 경로를 파싱해 허용 경로면 통과시킨다.
|
|
93
|
+
- **툴이 없다는 이유로 초안 생성을 포기하지 않는다.** 둘 중 실제로 존재하는 툴을 골라 즉시 실행하고, 차단 메시지를 받으면 그 메시지가 지시하는 형식으로 재시도한다.
|
|
94
|
+
- 이 경로의 파일 쓰기는 planner READ-ONLY 원칙의 명시적 예외이며 훅이 허용한다 (issue #8).
|
|
93
95
|
|
|
94
96
|
초안 구조:
|
|
95
97
|
```markdown
|
|
@@ -104,36 +106,29 @@ mkdir -p .makdoong2-team/<이슈키>
|
|
|
104
106
|
```bash
|
|
105
107
|
# state.json 에는 반드시 상대경로만 저장한다 (절대경로 저장 시 다른 cwd 에서 접근 불가 → Read hang 유발).
|
|
106
108
|
# 이 마커는 §2-5b 의 spec_hash 와 **한 쌍**이다 — spec_hash 만 기록되고 이것이 빠지면
|
|
107
|
-
#
|
|
109
|
+
# stage-analysis-verify.sh 가 2_implementation.analysis 진입을 하드 차단한다 (issue #6-①).
|
|
108
110
|
bash <SCRIPTS_DIR>/state.sh set <이슈키> \
|
|
109
111
|
'.stages."1_planning".substages."requirements".draft_path' '".makdoong2-team/<이슈키>/requirements-draft.md"'
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
### 2-3. 다출처 교차 조사
|
|
114
|
+
### 2-3. 다출처 교차 조사
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
**당신의 세션에서 직접 조사한다.** 소스마다 `skill(name=...)` 로 스킬을 먼저 로드한 뒤 그 스킬의 MCP 를 호출한다. 로드 전에 `skill_mcp` 를 부르면 `MCP server "<name>" not found` 로 실패한다. **outer-world 에이전트 위임 금지.**
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{source: "jira", focus: "에픽/상위 이슈, 링크 이슈, 관련 코멘트에서 구체화된 요구"},
|
|
123
|
-
{source: "confluence", focus: "관련 설계 문서, ADR, API 스펙, 운영 가이드"},
|
|
124
|
-
{source: "bitbucket", focus: "수정 대상 파일/클래스 현재 구현, 관련 PR 이력, 테스트 패턴"}
|
|
125
|
-
]
|
|
126
|
-
)
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
조사 세션은 서로를 보지 못한다. **한 focus 가 다른 조사 결과에 의존하면 안 된다** — 의존이 필요하면 라운드를 나눠 두 번 호출한다.
|
|
118
|
+
| 소스 | skill | mcp_name | 조사 범위 |
|
|
119
|
+
|---|---|---|---|
|
|
120
|
+
| Jira | `jira-research` | `works` | 에픽·상위 이슈·링크 이슈·서브태스크·코멘트에서 구체화된 요구와 결정 사항 |
|
|
121
|
+
| Confluence | `confluence-research` | `docs` | 설계 문서·ADR·API 스펙·운영 가이드의 제약과 합의된 규약 |
|
|
122
|
+
| Bitbucket | `bitbucket-research` | `repos` | 수정 대상 파일·클래스의 현재 구현, 관련 PR 이력, 기존 테스트 패턴 |
|
|
123
|
+
| GitHub OSS | `github-oss-research` | (없음 — WebFetch) | 외부 오픈소스의 사용 예시·알려진 이슈·업스트림 변경 |
|
|
130
124
|
|
|
131
|
-
|
|
125
|
+
기본 조사는 **Jira → Bitbucket** 두 소스다. 설계·규약이 쟁점이면 Confluence 를, 외부 라이브러리가 쟁점이면 GitHub OSS 를 더한다. Simple 이슈는 Jira 하나로 끝낼 수 있다.
|
|
132
126
|
|
|
133
|
-
|
|
127
|
+
**조사 예산은 소스당 최대 5회 호출이다.** 초과하면 그 소스는 거기서 멈추고 미확인 항목을 `미결 사항` 에 적는다.
|
|
134
128
|
|
|
135
|
-
-
|
|
136
|
-
- **조사 완결성을 이유로 마커 기록을 미루지 않는다 (hardrule).** 조사가 부분적이면
|
|
129
|
+
- **부분 조사는 그 자체로 정상 종료다.** 한 소스가 응답하지 않으면 그 소스를 포기하고 나머지로 진행한다. 결손을 메우려 같은 소스를 계속 두드리다 세션 예산을 전부 소진하고 **마커를 하나도 남기지 못한 채** 종료한 사고가 이틀 연속 재현됐다 (GitHub issue #9, 각 27분·17분 소모).
|
|
130
|
+
- **조사 완결성을 이유로 마커 기록을 미루지 않는다 (hardrule).** 조사가 부분적이면 미확인 항목을 초안의 `미결 사항` 에 남기고 **그 상태 그대로** 산출물과 substage 마커를 기록한 뒤 종료한다. 마커가 없는 종료는 상위에서 `completion: "incomplete"` 로 분류되어 substage 전체가 재실행된다 — 부분 결과까지 함께 버려진다.
|
|
131
|
+
- 조사 결과는 별도 파일로 만들지 않는다. `requirements-draft.md` 의 `## 수집된 정보` 에 소스별로 정리한다 (출처 URL 포함).
|
|
137
132
|
|
|
138
133
|
### 2-4. 요구사항 체크리스트 확인
|
|
139
134
|
|
|
@@ -176,7 +171,7 @@ category = (criticality == "critical" OR scope_size == "large") ? "major" : base
|
|
|
176
171
|
|
|
177
172
|
```bash
|
|
178
173
|
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy' \
|
|
179
|
-
'{"intent_type":"Standard","change_type":"bugfix","scope_size":"small","criticality":"normal","category":"minor","auto_approve":{"1_planning.requirements":true,"
|
|
174
|
+
'{"intent_type":"Standard","change_type":"bugfix","scope_size":"small","criticality":"normal","category":"minor","auto_approve":{"1_planning.requirements":true,"3_delivery.commit":true,"3_delivery.pr":true},"rationale":"<한 줄 근거>","categorized_by":"1_planning.requirements"}'
|
|
180
175
|
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy.categorized_at' "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
|
|
181
176
|
```
|
|
182
177
|
|
|
@@ -184,7 +179,7 @@ major 로 판정된 경우에도 `auto_approve` 맵은 **모두 true** 로 두
|
|
|
184
179
|
|
|
185
180
|
### 2-5b. 요구사항 품질 마커 (ambiguity_score · spec_hash)
|
|
186
181
|
|
|
187
|
-
`
|
|
182
|
+
`stage-analysis-verify.sh` 의 요구사항 품질 게이트는 **이 두 마커가 있을 때만** 검사한다
|
|
188
183
|
(구형 state 호환을 위한 조건부 검사). 즉 여기서 기록하지 않으면 통합 경로에서는
|
|
189
184
|
품질 게이트가 통째로 사문화된다 — `02-requirements.md` 를 거치는 분리 경로에서는
|
|
190
185
|
검사되는데 이 경로에서만 안 되는 비대칭이 생긴다. 반드시 기록한다.
|
|
@@ -206,23 +201,9 @@ bash <SCRIPTS_DIR>/state.sh get <이슈키> '.stages."1_planning".substages."req
|
|
|
206
201
|
bash <SCRIPTS_DIR>/state.sh get <이슈키> '.stages."1_planning".substages."requirements".spec_hash'
|
|
207
202
|
```
|
|
208
203
|
|
|
209
|
-
### 2-
|
|
204
|
+
### 2-5c. 개발 범위 확정 (구 `1_planning.scope` — 흡수됨)
|
|
210
205
|
|
|
211
|
-
|
|
212
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".self_check' \
|
|
213
|
-
'{"checklist_complete":true,"conflicts_resolved":true,"user_confirmed":true,"scope_clean":true,"draft_synced":true,"categorized":true,"ambiguity_scored":true,"spec_frozen":true,"draft_recorded":true}'
|
|
214
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".verification_pending' 'false'
|
|
215
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".done' 'true'
|
|
216
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".done_at' "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Phase 3: 개발 범위 파악 (`1_planning.scope`)
|
|
222
|
-
|
|
223
|
-
2단계 조사 결과로 코드 수정 계획을 수립한다. 2단계 `bitbucket-research` 탐색을 이어서 사용한다.
|
|
224
|
-
|
|
225
|
-
### 3-1. 범위 출력
|
|
206
|
+
범위 확정은 요구사항 확정과 **같은 판단의 연속**이라 별도 substage 를 두지 않는다. 조사 결과로 코드 수정 계획을 여기서 함께 확정한다. §2-3 의 `bitbucket-research` 탐색을 이어서 사용한다.
|
|
226
207
|
|
|
227
208
|
```
|
|
228
209
|
### 개발 범위
|
|
@@ -234,27 +215,27 @@ bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."req
|
|
|
234
215
|
**스코프 아웃**: <이번 이슈에서 다루지 않는 것>
|
|
235
216
|
```
|
|
236
217
|
|
|
237
|
-
|
|
218
|
+
이 블록은 `requirements-draft.md` 에도 그대로 남긴다 — dev 단계가 초안을 읽고 작업 단위를 잡는다.
|
|
238
219
|
|
|
239
|
-
실제 수정/추가 파일과 작업 단위가 확정된
|
|
220
|
+
**범주 재평가 (escalation — 하향 금지)**: 실제 수정/추가 파일과 작업 단위가 확정된 뒤 `scope_size`·`criticality` 를 재평가한다. minor → major 상향인 경우에만 갱신하며, `auto_approve` 맵은 건드리지 않고 **모두 true 로 유지**한다 — 상향은 위험도 라벨 정정에 그치고 흐름은 무인 진행을 유지한다:
|
|
240
221
|
|
|
241
|
-
minor → major로 상향될 경우에만 아래 항목들을 갱신한다. `auto_approve` 맵은 건드리지 않고 **모두 true 로 유지**한다 — 상향은 위험도 라벨 정정에 그치며 흐름 자체는 무인 진행으로 유지된다:
|
|
242
222
|
```bash
|
|
243
223
|
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy.category' '"major"'
|
|
244
224
|
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy.scope_size' '"large"'
|
|
245
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy.categorized_by' '"1_planning.
|
|
225
|
+
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.policy.categorized_by' '"1_planning.requirements"'
|
|
246
226
|
```
|
|
247
227
|
|
|
248
|
-
###
|
|
228
|
+
### 2-6. Requirements 완료 기록
|
|
249
229
|
|
|
250
230
|
```bash
|
|
251
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."
|
|
252
|
-
'{"
|
|
253
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."
|
|
254
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."
|
|
255
|
-
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."
|
|
231
|
+
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".self_check' \
|
|
232
|
+
'{"checklist_complete":true,"conflicts_resolved":true,"user_confirmed":true,"scope_clean":true,"draft_synced":true,"categorized":true,"ambiguity_scored":true,"spec_frozen":true,"draft_recorded":true,"paths_explicit":true,"test_scope_defined":true,"atomic_units":true,"scope_out_listed":true}'
|
|
233
|
+
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".verification_pending' 'false'
|
|
234
|
+
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".done' 'true'
|
|
235
|
+
bash <SCRIPTS_DIR>/state.sh set <이슈키> '.stages."1_planning".substages."requirements".done_at' "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
|
|
256
236
|
```
|
|
257
237
|
|
|
238
|
+
|
|
258
239
|
---
|
|
259
240
|
|
|
260
241
|
## 최종 출력
|