makdoong2-team 1.3.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/LICENSE +21 -0
- package/README.md +193 -0
- package/agents/makdoong2-analyzer.md +135 -0
- package/agents/makdoong2-engineer.md +165 -0
- package/agents/makdoong2-planner.md +267 -0
- package/agents/makdoong2-publisher.md +481 -0
- package/agents/makdoong2-team-leader.md +249 -0
- package/agents/makdoong2-verifier.md +353 -0
- package/assets/makdoong2-team.default.json +46 -0
- package/assets/makdoong2-team.schema.json +357 -0
- package/bin/cli.js +404 -0
- package/dist/agent-stage-config.d.ts +15 -0
- package/dist/agent-stage-config.js +119 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +96 -0
- package/dist/logger.d.ts +14 -0
- package/dist/logger.js +131 -0
- package/dist/mcp-secret-injector.d.ts +56 -0
- package/dist/mcp-secret-injector.js +89 -0
- package/dist/model-chain-cli.d.ts +1 -0
- package/dist/model-chain-cli.js +21 -0
- package/dist/model-fallback-policy.d.ts +69 -0
- package/dist/model-fallback-policy.js +211 -0
- package/dist/opencode-plugin.d.ts +8 -0
- package/dist/opencode-plugin.js +2457 -0
- package/dist/poll-sub-session.d.ts +139 -0
- package/dist/poll-sub-session.js +494 -0
- package/dist/redact-secrets.d.ts +3 -0
- package/dist/redact-secrets.js +68 -0
- package/dist/session-index.d.ts +11 -0
- package/dist/session-index.js +71 -0
- package/dist/skill-mcp-registry.d.ts +59 -0
- package/dist/skill-mcp-registry.js +178 -0
- package/dist/stall-escalation.d.ts +1 -0
- package/dist/stall-escalation.js +22 -0
- package/dist/tmux-monitor.d.ts +193 -0
- package/dist/tmux-monitor.js +694 -0
- package/dist/verdict-hash.d.ts +1 -0
- package/dist/verdict-hash.js +62 -0
- package/gates/stage-analysis-verify.sh +84 -0
- package/gates/stage2-requirements-verify.sh +13 -0
- package/gates/stage3-scope-verify.sh +45 -0
- package/gates/stage4-dev-post-verify.sh +64 -0
- package/gates/stage4-dev-verify.sh +36 -0
- package/gates/stage5-coverage-verify.sh +36 -0
- package/gates/stage5-test-verify.sh +24 -0
- package/gates/stage6-commit-verify.sh +41 -0
- package/gates/stage6-post-commit-verify.sh +131 -0
- package/gates/stage7-post-pr-verify.sh +53 -0
- package/gates/stage7-pr-verify.sh +48 -0
- package/gates/stage8-post-review-verify.sh +84 -0
- package/gates/stage8-review-verify.sh +45 -0
- package/gates/verify.sh +44 -0
- package/opencode.json.example +40 -0
- package/package.json +84 -0
- package/postinstall.mjs +56 -0
- package/references/commit-convention.md +130 -0
- package/references/jira-issue-templates.md +203 -0
- package/references/pr-template.md +381 -0
- package/scripts/config.sh +46 -0
- package/scripts/coverage-record.sh +67 -0
- package/scripts/gate-policy-test.sh +152 -0
- package/scripts/install-lib.mjs +1029 -0
- package/scripts/lint-agent-prompts.sh +74 -0
- package/scripts/log-event.sh +44 -0
- package/scripts/model-policy.mjs +183 -0
- package/scripts/publish-if-changed.sh +207 -0
- package/scripts/release.sh +276 -0
- package/scripts/rollback-commits.sh +35 -0
- package/scripts/smoke-test.mjs +194 -0
- package/scripts/state.sh +192 -0
- package/scripts/test-postinstall.mjs +141 -0
- package/scripts/with-fallback.sh +56 -0
- package/scripts/wt-sync-ignored.sh +193 -0
- package/skills/_lib/load-secret.sh +149 -0
- package/skills/bamboo-ci/SKILL.md +81 -0
- package/skills/bamboo-ci/run-bamboo.sh +23 -0
- package/skills/bitbucket-research/SKILL.md +87 -0
- package/skills/bitbucket-research/run-repos.sh +23 -0
- package/skills/confluence-research/SKILL.md +75 -0
- package/skills/confluence-research/run-docs.sh +23 -0
- package/skills/github-oss-research/SKILL.md +59 -0
- package/skills/jira-research/SKILL.md +75 -0
- package/skills/jira-research/run-works.sh +23 -0
- package/src/hooks/guard-bash.sh +67 -0
- package/src/hooks/session-start.sh +96 -0
- package/src/hooks/sync-state.sh +47 -0
- package/stages/01-jira.md +43 -0
- package/stages/01-planning.md +229 -0
- package/stages/02-requirements.md +298 -0
- package/stages/03-scope.md +81 -0
- package/stages/04-analysis.md +281 -0
- package/stages/05-worktree-dev.md +124 -0
- package/stages/06-test.md +161 -0
- package/stages/07-commit.md +229 -0
- package/stages/08-pr.md +177 -0
- package/stages/09-review-comments.md +277 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function computeVerdictHash(raw, stage) {
|
|
3
|
+
if (typeof raw !== "string") {
|
|
4
|
+
return createHash("sha256").update(`__nonstring__::${stage}`).digest("hex").slice(0, 16);
|
|
5
|
+
}
|
|
6
|
+
const trimmed = raw.trim();
|
|
7
|
+
const parsed = parseVerdictJSON(trimmed);
|
|
8
|
+
if (parsed && Array.isArray(parsed.findings)) {
|
|
9
|
+
const items = parsed.findings
|
|
10
|
+
.map((f) => {
|
|
11
|
+
const severity = String(f?.severity ?? "").toLowerCase();
|
|
12
|
+
const key = String(f?.item ?? f?.key ?? f?.name ?? "").toLowerCase();
|
|
13
|
+
return `${severity}::${key}`;
|
|
14
|
+
})
|
|
15
|
+
.filter((s) => s !== "::")
|
|
16
|
+
.sort();
|
|
17
|
+
const signature = JSON.stringify({
|
|
18
|
+
stage,
|
|
19
|
+
verdict: String(parsed.verdict ?? parsed.status ?? "").toUpperCase(),
|
|
20
|
+
items,
|
|
21
|
+
});
|
|
22
|
+
return createHash("sha256").update(signature).digest("hex").slice(0, 16);
|
|
23
|
+
}
|
|
24
|
+
return createHash("sha256").update(trimmed.slice(0, 800)).digest("hex").slice(0, 16);
|
|
25
|
+
}
|
|
26
|
+
function parseVerdictJSON(raw) {
|
|
27
|
+
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
28
|
+
const candidates = [];
|
|
29
|
+
if (fenced)
|
|
30
|
+
candidates.push(fenced[1]);
|
|
31
|
+
const firstBrace = raw.indexOf("{");
|
|
32
|
+
if (firstBrace >= 0) {
|
|
33
|
+
const tail = raw.slice(firstBrace);
|
|
34
|
+
let depth = 0;
|
|
35
|
+
let end = -1;
|
|
36
|
+
for (let i = 0; i < tail.length; i++) {
|
|
37
|
+
const ch = tail[i];
|
|
38
|
+
if (ch === "{")
|
|
39
|
+
depth++;
|
|
40
|
+
else if (ch === "}") {
|
|
41
|
+
depth--;
|
|
42
|
+
if (depth === 0) {
|
|
43
|
+
end = i + 1;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (end > 0)
|
|
49
|
+
candidates.push(tail.slice(0, end));
|
|
50
|
+
}
|
|
51
|
+
for (const c of candidates) {
|
|
52
|
+
try {
|
|
53
|
+
const obj = JSON.parse(c.trim());
|
|
54
|
+
if (obj && typeof obj === "object")
|
|
55
|
+
return obj;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* candidate not valid JSON — try next */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage-analysis-verify.sh — 2_implementation.analysis 진입 게이트 + SKIP 판정
|
|
3
|
+
#
|
|
4
|
+
# 사용: stage-analysis-verify.sh <issue>
|
|
5
|
+
#
|
|
6
|
+
# 진입 조건:
|
|
7
|
+
# 1. state.json 존재
|
|
8
|
+
# 2. 1_planning.scope substage done=true
|
|
9
|
+
#
|
|
10
|
+
# SKIP 조건:
|
|
11
|
+
# 프로젝트 루트에 build tool 마커 파일이 하나도 없음
|
|
12
|
+
# → state.json 에 skipped=true, skip_reason, done=true 마킹 후 exit 0
|
|
13
|
+
#
|
|
14
|
+
# 의존: jq, git (state.sh 내부에서 사용)
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
ISSUE="${1:?usage: stage-analysis-verify.sh <issue>}"
|
|
18
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
+
SCRIPTS="$HERE/../scripts"
|
|
20
|
+
|
|
21
|
+
fail() { echo "MAKDOONG2-GATE BLOCKED [2_implementation.analysis]: $*" >&2; exit 2; }
|
|
22
|
+
|
|
23
|
+
q() {
|
|
24
|
+
"$SCRIPTS/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo ""
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# 1. state.json 존재 확인
|
|
28
|
+
[ -n "$(q '.issue')" ] || fail "state.json 없음. 1_planning 단계 먼저 완료 필요."
|
|
29
|
+
|
|
30
|
+
# 2. 이미 done=true 인 경우 재-dispatch 차단 (SKIP 마킹으로 done 이 이미 세팅되었을 수 있음)
|
|
31
|
+
ANALYSIS_DONE="$(q '.stages."2_implementation".substages."analysis".done')"
|
|
32
|
+
[ "$ANALYSIS_DONE" != "true" ] || fail "이미 done=true 완료됨 — auto_advance_stage 로 다음 단계 진행"
|
|
33
|
+
|
|
34
|
+
# 3. scope substage 완료 확인
|
|
35
|
+
SCOPE_DONE="$(q '.stages."1_planning".substages."scope".done')"
|
|
36
|
+
[ "$SCOPE_DONE" = "true" ] || fail "1_planning.scope 미완료 (done=$SCOPE_DONE)"
|
|
37
|
+
|
|
38
|
+
# 3. SKIP 판정: worktree 루트에서 build tool 마커 파일 존재 여부
|
|
39
|
+
WT="$(q '.worktree' | tr -d '"')"
|
|
40
|
+
[ -d "$WT" ] || WT="$(pwd)"
|
|
41
|
+
|
|
42
|
+
MARKERS=(
|
|
43
|
+
"package.json"
|
|
44
|
+
"build.gradle"
|
|
45
|
+
"build.gradle.kts"
|
|
46
|
+
"pom.xml"
|
|
47
|
+
"Cargo.toml"
|
|
48
|
+
"go.mod"
|
|
49
|
+
"pyproject.toml"
|
|
50
|
+
"setup.py"
|
|
51
|
+
"requirements.txt"
|
|
52
|
+
"Gemfile"
|
|
53
|
+
"mix.exs"
|
|
54
|
+
"build.sbt"
|
|
55
|
+
"composer.json"
|
|
56
|
+
"Package.swift"
|
|
57
|
+
"Makefile"
|
|
58
|
+
"CMakeLists.txt"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
FOUND=""
|
|
62
|
+
for m in "${MARKERS[@]}"; do
|
|
63
|
+
if [ -f "$WT/$m" ]; then FOUND="$m"; break; fi
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
# .csproj 는 glob 확인
|
|
67
|
+
if [ -z "$FOUND" ]; then
|
|
68
|
+
if compgen -G "$WT/*.csproj" > /dev/null 2>&1; then FOUND=".csproj"; fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ -z "$FOUND" ]; then
|
|
72
|
+
# SKIP: state.json 마킹 후 exit 0
|
|
73
|
+
"$SCRIPTS/state.sh" set "$ISSUE" \
|
|
74
|
+
'.stages."2_implementation".substages."analysis".skipped' 'true' > /dev/null
|
|
75
|
+
"$SCRIPTS/state.sh" set "$ISSUE" \
|
|
76
|
+
'.stages."2_implementation".substages."analysis".skip_reason' \
|
|
77
|
+
'"no_build_tool_marker"' > /dev/null
|
|
78
|
+
"$SCRIPTS/state.sh" set "$ISSUE" \
|
|
79
|
+
'.stages."2_implementation".substages."analysis".done' 'true' > /dev/null
|
|
80
|
+
echo "MAKDOONG2-GATE SKIP: 2_implementation.analysis (no build tool marker in $WT)"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.analysis (marker=$FOUND)"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage2-requirements-verify.sh <issue> — 2단계(요구사항) 진입 게이트
|
|
3
|
+
# 의존: jq
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
ISSUE="${1:?usage: stage2-requirements-verify.sh <issue>}"
|
|
7
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
8
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [1_planning.requirements]: $1" >&2; exit 2; }
|
|
9
|
+
[ -f "$P" ] || fail "state.json 없음 — jira substage부터 시작하라"
|
|
10
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
11
|
+
[ "$(q '.stages."1_planning".substages."requirements".done')" != "true" ] || fail "이미 done=true 완료됨 — auto_advance_stage 로 다음 단계 진행"
|
|
12
|
+
[ "$(q '.stages."1_planning".substages."jira".done')" = "true" ] || fail "jira substage 미완료"
|
|
13
|
+
echo "MAKDOONG2-GATE OK: 1_planning.requirements"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage3-scope-verify.sh <issue> — 3단계(범위 확정) 진입 게이트
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
ISSUE="${1:?usage: stage3-scope-verify.sh <issue>}"
|
|
6
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
7
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [1_planning.scope]: $1" >&2; exit 2; }
|
|
8
|
+
[ -f "$P" ] || fail "state.json 없음 — jira substage부터 시작하라"
|
|
9
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
10
|
+
[ "$(q '.stages."1_planning".substages."scope".done')" != "true" ] || fail "이미 done=true 완료됨 — auto_advance_stage 로 다음 단계 진행"
|
|
11
|
+
[ "$(q '.stages."1_planning".substages."requirements".done')" = "true" ] || fail "requirements substage 미완료"
|
|
12
|
+
|
|
13
|
+
if [ "$(q '.policy.auto_approve."1_planning.requirements"')" != "true" ]; then
|
|
14
|
+
[ "$(q '.stages."1_planning".substages."requirements".approved_by_user')" = "true" ] \
|
|
15
|
+
|| fail "requirements substage 사용자 승인 없음 (또는 .policy.auto_approve.\"1_planning.requirements\" 미설정)"
|
|
16
|
+
if [ "$(q '.stages."1_planning".substages."requirements".verification_pending')" = "true" ]; then
|
|
17
|
+
fail "requirements substage 검증 대기 중 (verification_pending) — 사용자 승인 후 approved_by_user를 기록하라"
|
|
18
|
+
fi
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if [ "$(q '.stages."1_planning".substages."requirements".interview_required')" = "true" ]; then
|
|
22
|
+
[ "$(q '.stages."1_planning".substages."requirements".interview_completed')" = "true" ] \
|
|
23
|
+
|| fail "requirements substage 인터뷰 미완료 — 모든 미결 항목 해소 후 interview_completed=true 기록 필요"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# --- 요구사항 품질 게이트 (조건부 — 마커 존재 시에만 검사, 구형 state 호환) ---
|
|
27
|
+
# 1) Ambiguity Score 수렴: 기록된 값이 0.2 초과면 요구사항 미수렴 (stages/02-requirements.md §2-3-2b)
|
|
28
|
+
AMB="$(q '.stages."1_planning".substages."requirements".ambiguity_score')"
|
|
29
|
+
if [ "$AMB" != "__MISSING__" ] && [ "$AMB" != "null" ] && [ -n "$AMB" ]; then
|
|
30
|
+
awk -v a="$AMB" 'BEGIN { exit (a + 0 <= 0.2) ? 0 : 1 }' \
|
|
31
|
+
|| fail "ambiguity_score=$AMB > 0.2 — 요구사항 미수렴. 인터뷰로 미결 항목 해소 후 재산정하라"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# 2) 명세 동결(spec drift) 검증: spec_hash 기록 시 draft 파일 해시 재계산 일치 필요 (§2-4a)
|
|
35
|
+
SPEC_HASH="$(q '.stages."1_planning".substages."requirements".spec_hash')"
|
|
36
|
+
if [ "$SPEC_HASH" != "__MISSING__" ] && [ "$SPEC_HASH" != "null" ] && [ -n "$SPEC_HASH" ]; then
|
|
37
|
+
DRAFT="$(q '.stages."1_planning".substages."requirements".draft_path')"
|
|
38
|
+
ROOT="$("$HERE/../scripts/state.sh" root)"
|
|
39
|
+
{ [ "$DRAFT" != "__MISSING__" ] && [ "$DRAFT" != "null" ] && [ -f "$ROOT/$DRAFT" ]; } \
|
|
40
|
+
|| fail "spec_hash 기록됨 그러나 확정 명세 파일 없음 (draft_path=$DRAFT)"
|
|
41
|
+
ACTUAL="$(sha256sum "$ROOT/$DRAFT" | cut -d' ' -f1)"
|
|
42
|
+
[ "$ACTUAL" = "$SPEC_HASH" ] \
|
|
43
|
+
|| fail "확정 명세 무단 변경 감지 (spec drift) — 동결 후 변경은 사용자 재승인 + spec_hash 재기록 절차만 허용 (stages/02-requirements.md §2-4a)"
|
|
44
|
+
fi
|
|
45
|
+
echo "MAKDOONG2-GATE OK: 1_planning.scope"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage4-dev-post-verify.sh <issue> — 4단계(개발) 종료 게이트.
|
|
3
|
+
# ─────────────────────────────────────────────────────────
|
|
4
|
+
# 검증 항목:
|
|
5
|
+
# 1. worktree 가 존재하는가
|
|
6
|
+
# 2. dev-written-files.txt 에 기록된 모든 파일이 staging(index) 혹은 HEAD tree 에 존재하는가
|
|
7
|
+
# 3. .gitignore 를 존중한 untracked 파일이 0인가
|
|
8
|
+
#
|
|
9
|
+
# 불변식: 3_delivery.commit 는 untracked 를 자동 제외하므로,
|
|
10
|
+
# 본 게이트를 통과한 파일만이 커밋 대상에 진입한다.
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
ISSUE="${1:?usage: stage4-dev-post-verify.sh <issue>}"
|
|
14
|
+
|
|
15
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [2_implementation.dev_post]: $1" >&2; exit 2; }
|
|
16
|
+
|
|
17
|
+
WT="$("$HERE/../scripts/state.sh" get "$ISSUE" '.worktree' 2>/dev/null | tr -d '"' || echo "")"
|
|
18
|
+
[ -n "$WT" ] && [ -d "$WT" ] || fail "worktree 경로 부재 또는 접근 불가: $WT"
|
|
19
|
+
|
|
20
|
+
TRACK="$WT/.makdoong2-team/$ISSUE/dev-written-files.txt"
|
|
21
|
+
|
|
22
|
+
MISSING=""
|
|
23
|
+
if [ -f "$TRACK" ]; then
|
|
24
|
+
while IFS= read -r F; do
|
|
25
|
+
[ -z "$F" ] && continue
|
|
26
|
+
if git -C "$WT" diff --cached --name-only | grep -qxF "$F"; then
|
|
27
|
+
continue
|
|
28
|
+
fi
|
|
29
|
+
if git -C "$WT" ls-tree -r --name-only HEAD 2>/dev/null | grep -qxF "$F"; then
|
|
30
|
+
continue
|
|
31
|
+
fi
|
|
32
|
+
if [ ! -e "$WT/$F" ]; then
|
|
33
|
+
continue
|
|
34
|
+
fi
|
|
35
|
+
MISSING="${MISSING}
|
|
36
|
+
- $F"
|
|
37
|
+
done < <(sort -u "$TRACK")
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
UNTRACKED="$(git -C "$WT" ls-files --others --exclude-standard 2>/dev/null || true)"
|
|
41
|
+
|
|
42
|
+
if [ -n "$MISSING" ] || [ -n "$UNTRACKED" ]; then
|
|
43
|
+
MSG="Engineer 가 편집한 파일이 staging area 에 포함되지 않았다."
|
|
44
|
+
MSG="${MSG}
|
|
45
|
+
Publisher(3_delivery.commit) 가 untracked 파일을 자동 제외하므로 여기서 통과하지 못하면 신규 소스코드가 커밋되지 않는다."
|
|
46
|
+
if [ -n "$MISSING" ] && [ -f "$TRACK" ]; then
|
|
47
|
+
MSG="${MSG}
|
|
48
|
+
|
|
49
|
+
[dev-written-files.txt 에 기록됐지만 staged 아님]${MISSING}"
|
|
50
|
+
fi
|
|
51
|
+
if [ -n "$UNTRACKED" ]; then
|
|
52
|
+
MSG="${MSG}
|
|
53
|
+
|
|
54
|
+
[.gitignore 존중 untracked 파일 (안전망 감지)]
|
|
55
|
+
$(echo "$UNTRACKED" | sed 's/^/ - /')"
|
|
56
|
+
fi
|
|
57
|
+
MSG="${MSG}
|
|
58
|
+
|
|
59
|
+
→ 조치: worktree($WT) 에서 각 파일에 'git add -- <파일>' 실행 후 dev.done 재기록.
|
|
60
|
+
→ 자동화 필요: tool.execute.after 훅이 auto git add 를 수행해야 하는데 미동작. 훅 상태 점검 필요."
|
|
61
|
+
fail "$MSG"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.dev_post"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage4-dev-verify.sh <issue> — 4단계(개발) 진입 게이트
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
ISSUE="${1:?usage: stage4-dev-verify.sh <issue>}"
|
|
6
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
7
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [2_implementation.dev]: $1" >&2; exit 2; }
|
|
8
|
+
[ -f "$P" ] || fail "state.json 없음 — planning phase부터 시작하라"
|
|
9
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
10
|
+
assert_worktree_sibling(){
|
|
11
|
+
local wt="$1" main pwt pmain
|
|
12
|
+
[ -d "$wt" ] || fail "worktree 경로가 존재하지 않음: $wt"
|
|
13
|
+
main="$(git -C "$wt" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
|
|
14
|
+
[ -n "$main" ] || fail "메인 repo 식별 실패"
|
|
15
|
+
[ "$wt" != "$main" ] || fail "worktree가 메인 repo와 동일 경로 — dev 단계는 별도 worktree 필요 (메인: $main)"
|
|
16
|
+
pwt="$(cd "$(dirname "$wt")" && pwd -P)"
|
|
17
|
+
pmain="$(cd "$(dirname "$main")" && pwd -P)"
|
|
18
|
+
[ "$pwt" = "$pmain" ] || fail "worktree가 메인 repo의 형제 디렉토리가 아님: $wt (메인: $main)"
|
|
19
|
+
}
|
|
20
|
+
[ "$(q '.stages."2_implementation".substages."dev".done')" != "true" ] || fail "이미 done=true 완료됨 — auto_advance_stage 로 다음 단계 진행"
|
|
21
|
+
[ "$(q '.stages."1_planning".substages."scope".done')" = "true" ] || fail "scope substage 미완료"
|
|
22
|
+
# analysis substage 는 게이트가 SKIP 시 done=true 로 마킹하므로 skipped 여부와 무관하게 done=true 검사만 하면 된다.
|
|
23
|
+
[ "$(q '.stages."2_implementation".substages."analysis".done')" = "true" ] || fail "analysis substage 미완료 (skipped 도 done=true 로 처리됨)"
|
|
24
|
+
|
|
25
|
+
WT="$(q '.worktree')"
|
|
26
|
+
[ -n "$WT" ] && [ "$WT" != "__MISSING__" ] || fail "worktree 경로 미설정 — state.json의 .worktree 필드 확인"
|
|
27
|
+
assert_worktree_sibling "$WT"
|
|
28
|
+
|
|
29
|
+
if [ "$(q '.policy.auto_approve."1_planning.scope"')" != "true" ]; then
|
|
30
|
+
[ "$(q '.stages."1_planning".substages."scope".approved_by_user')" = "true" ] \
|
|
31
|
+
|| fail "scope substage 사용자 승인 없음 (또는 .policy.auto_approve.\"1_planning.scope\" 미설정)"
|
|
32
|
+
if [ "$(q '.stages."1_planning".substages."scope".verification_pending')" = "true" ]; then
|
|
33
|
+
fail "scope substage 검증 대기 중 (verification_pending) — 사용자 승인 후 approved_by_user를 기록하라"
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.dev"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage5-coverage-verify.sh <issue>
|
|
3
|
+
# 수정된 코드의 테스트 커버리지가 임계값 이상인지 검증한다.
|
|
4
|
+
# 임계값은 makdoong2-team.json 의 coverage.threshold 로 조정 (기본: 95).
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
ISSUE="${1:?usage: stage5-coverage-verify.sh <issue>}"
|
|
8
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
9
|
+
THRESHOLD="$("$HERE/../scripts/config.sh" get coverage.threshold 95)"
|
|
10
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [2_implementation.coverage]: $1" >&2; exit 2; }
|
|
11
|
+
[ -f "$P" ] || fail "state.json 없음"
|
|
12
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
13
|
+
|
|
14
|
+
COV="$(q '.stages."2_implementation".substages."test".coverage')"
|
|
15
|
+
ATTEMPT="$(q '.stages."2_implementation".substages."test".coverage_attempt')"
|
|
16
|
+
[ "$ATTEMPT" = "__MISSING__" ] && ATTEMPT=1
|
|
17
|
+
|
|
18
|
+
case "$COV" in
|
|
19
|
+
pass)
|
|
20
|
+
PCT="$(q '.stages."2_implementation".substages."test".coverage_pct')"
|
|
21
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.coverage (${PCT}% >= ${THRESHOLD}%, 시도 ${ATTEMPT}/2)"
|
|
22
|
+
;;
|
|
23
|
+
exempt)
|
|
24
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.coverage (exempt — 사용자 승인됨, 시도 ${ATTEMPT}/2)"
|
|
25
|
+
;;
|
|
26
|
+
fail)
|
|
27
|
+
PCT="$(q '.stages."2_implementation".substages."test".coverage_pct')"
|
|
28
|
+
fail "커버리지 미달 (${PCT}% < ${THRESHOLD}%, 시도 ${ATTEMPT}/2, 최대 2라운드) — 테스트를 추가하거나 사용자 승인 후 exempt 처리"
|
|
29
|
+
;;
|
|
30
|
+
__MISSING__)
|
|
31
|
+
fail "커버리지 미측정 — test substage에서 커버리지를 측정하고 state.json에 기록하라"
|
|
32
|
+
;;
|
|
33
|
+
*)
|
|
34
|
+
fail "coverage 상태 불명확 ($COV) — pass / fail / exempt 중 하나여야 함"
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage5-test-verify.sh <issue> — 5단계(테스트) 진입 게이트
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
ISSUE="${1:?usage: stage5-test-verify.sh <issue>}"
|
|
6
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
7
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [2_implementation.test]: $1" >&2; exit 2; }
|
|
8
|
+
[ -f "$P" ] || fail "state.json 없음 — planning phase부터 시작하라"
|
|
9
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
10
|
+
assert_worktree_sibling(){
|
|
11
|
+
local wt="$1" main pwt pmain
|
|
12
|
+
[ -d "$wt" ] || fail "worktree 경로가 존재하지 않음: $wt"
|
|
13
|
+
main="$(git -C "$wt" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
|
|
14
|
+
[ -n "$main" ] || fail "메인 repo 식별 실패"
|
|
15
|
+
[ "$wt" != "$main" ] || fail "worktree가 메인 repo와 동일 경로 — 별도 worktree 필요"
|
|
16
|
+
pwt="$(cd "$(dirname "$wt")" && pwd -P)"
|
|
17
|
+
pmain="$(cd "$(dirname "$main")" && pwd -P)"
|
|
18
|
+
[ "$pwt" = "$pmain" ] || fail "worktree가 메인 repo의 형제 디렉토리가 아님: $wt (메인: $main)"
|
|
19
|
+
}
|
|
20
|
+
[ "$(q '.stages."2_implementation".substages."test".done')" != "true" ] || fail "이미 done=true 완료됨 — auto_advance_stage 로 다음 단계 진행"
|
|
21
|
+
[ "$(q '.stages."2_implementation".substages."dev".done')" = "true" ] || fail "dev substage 미완료"
|
|
22
|
+
WT="$(q '.worktree')"
|
|
23
|
+
assert_worktree_sibling "$WT"
|
|
24
|
+
echo "MAKDOONG2-GATE OK: 2_implementation.test"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage6-commit-verify.sh <issue> — 6단계(커밋) 진입 게이트
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
ISSUE="${1:?usage: stage6-commit-verify.sh <issue>}"
|
|
6
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
7
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [3_delivery.commit]: $1" >&2; exit 2; }
|
|
8
|
+
[ -f "$P" ] || fail "state.json 없음 — planning phase부터 시작하라"
|
|
9
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
10
|
+
U="$(q '.stages."2_implementation".substages."test".unit')"
|
|
11
|
+
case "$U" in
|
|
12
|
+
pass|skip) : ;;
|
|
13
|
+
fail) fail "단위 테스트 실패 — dev substage로 복귀해 수정하라" ;;
|
|
14
|
+
*) fail "단위 테스트 결과 미기록(unit=$U) — test substage 미완료" ;;
|
|
15
|
+
esac
|
|
16
|
+
I="$(q '.stages."2_implementation".substages."test".integration')"
|
|
17
|
+
case "$I" in
|
|
18
|
+
pass|skip) : ;;
|
|
19
|
+
fail) fail "통합 테스트 실패 — dev substage로 복귀해 수정하라" ;;
|
|
20
|
+
*) fail "통합 테스트 결과 미기록(integration=$I) — test substage 미완료" ;;
|
|
21
|
+
esac
|
|
22
|
+
"$HERE/stage5-coverage-verify.sh" "$ISSUE"
|
|
23
|
+
|
|
24
|
+
if [ "$(jq -r '.policy.auto_approve."3_delivery.commit"' "$P" 2>/dev/null)" = "false" ]; then
|
|
25
|
+
# [Check 1] verification_pending=true: 보고서 생성 완료, 사용자 승인 대기 중 → BLOCK
|
|
26
|
+
# (report 존재 여부보다 먼저 확인 — publisher가 보고서를 생성하고 pending을 세팅한 상태)
|
|
27
|
+
if [ "$(q '.stages."3_delivery".substages."commit".verification_pending')" = "true" ]; then
|
|
28
|
+
fail "major 이슈 — 커밋 승인 대기 중(verification_pending) — 사용자 승인 후 approved_by_user=true 기록 + verification_pending=false 해소"
|
|
29
|
+
fi
|
|
30
|
+
# [Check 2] 보고서가 있는데 미승인 → BLOCK
|
|
31
|
+
# (report 없음 = 최초 dispatch 시점 → PASS하여 publisher가 보고서를 생성할 수 있도록 허용)
|
|
32
|
+
REPORT="$(dirname "$P")/change-report.md"
|
|
33
|
+
if [ -f "$REPORT" ]; then
|
|
34
|
+
[ "$(q '.stages."3_delivery".substages."commit".approved_by_user')" = "true" ] \
|
|
35
|
+
|| fail "major 이슈 — 최종 커밋 사용자 미승인. 보고서 검토 후 .stages.\"3_delivery\".substages.\"commit\".approved_by_user=true를 기록하라"
|
|
36
|
+
fi
|
|
37
|
+
# [Check 3 — implicit] report 없음 + pending 없음 = 초기 상태 → PASS
|
|
38
|
+
# publisher 첫 dispatch가 진입하여 change-report.md를 생성한다.
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "MAKDOONG2-GATE OK: 3_delivery.commit"
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage6-post-commit-verify.sh <issue> — 6단계 커밋 완료 후 검증 게이트
|
|
3
|
+
# ─────────────────────────────────────────────────────────
|
|
4
|
+
# 검증 항목:
|
|
5
|
+
# 1. worktree 가 메인 repo 의 형제 디렉토리인지
|
|
6
|
+
# 2. base_sha 기록 + HEAD 조상 여부
|
|
7
|
+
# 3. base_sha..HEAD 커밋 수 > 0
|
|
8
|
+
# 4. 각 커밋이 정확히 1개 파일만 포함 (1 파일 = 1 commit 원칙)
|
|
9
|
+
# 5. 각 커밋 subject 에 결합어 (and / & / + / 및 / 그리고) 없음
|
|
10
|
+
# 6. 각 커밋 subject 가 Git Commit Guidelines 형식 준수
|
|
11
|
+
# - 형식: <Type>: <이슈키> - <요약> (제목 50자 이내, 마침표 없음)
|
|
12
|
+
# - Type: Feat/Fix/Chore/Refactor/Docs/Style/Test/Perf/Ci/Build/Revert
|
|
13
|
+
# - 이슈키 = 인자로 받은 $ISSUE 와 정확히 일치
|
|
14
|
+
# 7. 각 커밋 본문에 이슈 참조 마커 ([RV] <이슈키>) 포함
|
|
15
|
+
# 8. atomic_review.all_atomic == true + one_file_per_commit == true
|
|
16
|
+
# 9. atomic_review.count_commits == 실제 커밋 수
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
+
ISSUE="${1:?usage: stage6-post-commit-verify.sh <issue>}"
|
|
20
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
21
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [3_delivery.commit_post]: $1" >&2; exit 2; }
|
|
22
|
+
[ -f "$P" ] || fail "state.json 없음 — planning phase부터 시작하라"
|
|
23
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
24
|
+
|
|
25
|
+
assert_worktree_sibling(){
|
|
26
|
+
local wt="$1" main pwt pmain
|
|
27
|
+
[ -d "$wt" ] || fail "worktree 경로가 존재하지 않음: $wt"
|
|
28
|
+
main="$(git -C "$wt" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
|
|
29
|
+
[ -n "$main" ] || fail "메인 repo 식별 실패"
|
|
30
|
+
[ "$wt" != "$main" ] || fail "worktree가 메인 repo와 동일 경로 — 별도 worktree 필요"
|
|
31
|
+
pwt="$(cd "$(dirname "$wt")" && pwd -P)"
|
|
32
|
+
pmain="$(cd "$(dirname "$main")" && pwd -P)"
|
|
33
|
+
[ "$pwt" = "$pmain" ] || fail "worktree가 메인 repo의 형제 디렉토리가 아님: $wt (메인: $main)"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
WT="$(q '.worktree')"
|
|
37
|
+
assert_worktree_sibling "$WT"
|
|
38
|
+
|
|
39
|
+
# ─── §2. base_sha ───
|
|
40
|
+
BASE="$(q '.stages."3_delivery".substages."commit".base_sha')"
|
|
41
|
+
{ [ -n "$BASE" ] && [ "$BASE" != "null" ] && [ "$BASE" != "__MISSING__" ]; } || \
|
|
42
|
+
fail "commit substage base_sha 미기록 — commit substage 시작 시 git rev-parse HEAD를 base_sha로 기록해야 함"
|
|
43
|
+
git -C "$WT" merge-base --is-ancestor "$BASE" HEAD 2>/dev/null || \
|
|
44
|
+
fail "base_sha($BASE)가 현재 HEAD의 조상이 아님 — base_sha 또는 분기 상태 확인 필요"
|
|
45
|
+
|
|
46
|
+
# ─── §3. 커밋 수 > 0 ───
|
|
47
|
+
N=$(git -C "$WT" rev-list --count "$BASE..HEAD" 2>/dev/null || echo 0)
|
|
48
|
+
[ "$N" -gt 0 ] || fail "base_sha와 HEAD가 동일 — commit substage에서 만든 커밋이 없음"
|
|
49
|
+
|
|
50
|
+
# ─── §4. 1 파일 = 1 commit 원칙 (hardrule) ───
|
|
51
|
+
MULTI_FILE_VIOLATIONS=""
|
|
52
|
+
for SHA in $(git -C "$WT" rev-list "$BASE..HEAD"); do
|
|
53
|
+
NF=$(git -C "$WT" show --name-only --pretty="" "$SHA" | grep -c . || true)
|
|
54
|
+
if [ "$NF" -ne 1 ]; then
|
|
55
|
+
SUBJ=$(git -C "$WT" log -1 --format='%s' "$SHA")
|
|
56
|
+
MULTI_FILE_VIOLATIONS="${MULTI_FILE_VIOLATIONS}
|
|
57
|
+
- ${SHA:0:7} '${SUBJ}' 파일수=${NF} (기대: 1)"
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
[ -z "$MULTI_FILE_VIOLATIONS" ] || \
|
|
61
|
+
fail "1 파일 = 1 commit 원칙 위반. 각 커밋은 정확히 1개 파일만 포함해야 함:${MULTI_FILE_VIOLATIONS}
|
|
62
|
+
→ 조치: 'bash <SCRIPTS_DIR>/rollback-commits.sh $ISSUE' 후 파일별로 재커밋"
|
|
63
|
+
|
|
64
|
+
# ─── §5. 결합어 검사 ───
|
|
65
|
+
BAD=$(git -C "$WT" log --format='%s' "$BASE..HEAD" | grep -cE ' and | & | \+ | 및 | 그리고 ' || true)
|
|
66
|
+
[ "$BAD" -eq 0 ] || \
|
|
67
|
+
fail "커밋 subject에 결합어(and / & / + / 및 / 그리고) ${BAD}건 발견 — 1 commit = 1 change 위반 가능. git log로 확인 후 재구성하라"
|
|
68
|
+
|
|
69
|
+
# ─── §6. 커밋 메시지 형식 재검증 ───
|
|
70
|
+
# 형식: ^(Feat|Fix|Chore|Refactor|Docs|Style|Test|Perf|Ci|Build|Revert): <ISSUE> - <요약>$
|
|
71
|
+
# 제목 50자 이내, 마침표 없음
|
|
72
|
+
MSG_FMT_RE="^(Feat|Fix|Chore|Refactor|Docs|Style|Test|Perf|Ci|Build|Revert): ${ISSUE} - .+$"
|
|
73
|
+
MSG_VIOLATIONS=""
|
|
74
|
+
while IFS= read -r line; do
|
|
75
|
+
# line 형식: <SHA_short><TAB><subject>
|
|
76
|
+
SHA="${line%%$'\t'*}"
|
|
77
|
+
SUBJ="${line#*$'\t'}"
|
|
78
|
+
if ! [[ "$SUBJ" =~ $MSG_FMT_RE ]]; then
|
|
79
|
+
MSG_VIOLATIONS="${MSG_VIOLATIONS}
|
|
80
|
+
- ${SHA} '${SUBJ}' 형식 불일치 (기대: <Type>: ${ISSUE} - <요약>)"
|
|
81
|
+
continue
|
|
82
|
+
fi
|
|
83
|
+
# wc -m 은 로케일 기준 문자 수 (bash ${#SUBJ} 는 UTF-8 바이트 수로 한글 3배).
|
|
84
|
+
SUBJ_LEN=$(printf '%s' "$SUBJ" | wc -m)
|
|
85
|
+
if [ "$SUBJ_LEN" -gt 50 ]; then
|
|
86
|
+
MSG_VIOLATIONS="${MSG_VIOLATIONS}
|
|
87
|
+
- ${SHA} '${SUBJ}' 제목 ${SUBJ_LEN}자 초과 (최대 50자)"
|
|
88
|
+
fi
|
|
89
|
+
# 마침표 금지
|
|
90
|
+
if [[ "$SUBJ" == *. ]]; then
|
|
91
|
+
MSG_VIOLATIONS="${MSG_VIOLATIONS}
|
|
92
|
+
- ${SHA} '${SUBJ}' 제목 끝에 마침표 금지"
|
|
93
|
+
fi
|
|
94
|
+
done < <(git -C "$WT" log --format='%h%x09%s' "$BASE..HEAD")
|
|
95
|
+
|
|
96
|
+
[ -z "$MSG_VIOLATIONS" ] || \
|
|
97
|
+
fail "커밋 메시지 형식 위반:${MSG_VIOLATIONS}
|
|
98
|
+
→ 조치: git commit --amend 로 수정하거나 rollback-commits.sh 후 재작성"
|
|
99
|
+
|
|
100
|
+
# ─── §7. 이슈 참조 마커 확인 ───
|
|
101
|
+
# 커밋 body 어딘가에 [RV] <이슈키> 마커가 포함되어야 한다.
|
|
102
|
+
# body 가 빈 커밋은 허용하되 (선택), body 가 있다면 마커 필수.
|
|
103
|
+
KW_RE='\[RV\][[:space:]]+[A-Z]+-[0-9]+'
|
|
104
|
+
KW_VIOLATIONS=""
|
|
105
|
+
for SHA in $(git -C "$WT" rev-list "$BASE..HEAD"); do
|
|
106
|
+
BODY=$(git -C "$WT" log -1 --format='%b' "$SHA")
|
|
107
|
+
if [ -n "$(printf '%s' "$BODY" | tr -d '[:space:]')" ]; then
|
|
108
|
+
if ! printf '%s' "$BODY" | grep -qE "$KW_RE"; then
|
|
109
|
+
SUBJ=$(git -C "$WT" log -1 --format='%s' "$SHA")
|
|
110
|
+
KW_VIOLATIONS="${KW_VIOLATIONS}
|
|
111
|
+
- ${SHA:0:7} '${SUBJ}' 본문에 이슈 참조 마커 없음 ([RV] ${ISSUE} 필요)"
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
done
|
|
115
|
+
[ -z "$KW_VIOLATIONS" ] || \
|
|
116
|
+
fail "커밋 본문에 이슈 참조 마커 누락:${KW_VIOLATIONS}"
|
|
117
|
+
|
|
118
|
+
# ─── §8. atomic_review 마커 ───
|
|
119
|
+
A="$(q '.stages."3_delivery".substages."commit".atomic_review.all_atomic')"
|
|
120
|
+
[ "$A" = "true" ] || \
|
|
121
|
+
fail "atomic_review.all_atomic 미기록 또는 false — 각 커밋이 단일 변경인지 검토 후 true로 기록해야 함"
|
|
122
|
+
OFPC="$(q '.stages."3_delivery".substages."commit".atomic_review.one_file_per_commit')"
|
|
123
|
+
[ "$OFPC" = "true" ] || \
|
|
124
|
+
fail "atomic_review.one_file_per_commit 미기록 또는 false — 1 파일/commit 자체 검토 후 true로 기록해야 함"
|
|
125
|
+
|
|
126
|
+
# ─── §9. count_commits 일치 ───
|
|
127
|
+
C="$(q '.stages."3_delivery".substages."commit".atomic_review.count_commits')"
|
|
128
|
+
[ "$C" = "$N" ] || \
|
|
129
|
+
fail "atomic_review.count_commits=$C 와 실제 커밋 수 $N 불일치 — 다시 기록하라"
|
|
130
|
+
|
|
131
|
+
echo "MAKDOONG2-GATE OK: 3_delivery.commit_post (${N}개 커밋 모두 1파일/commit + 메시지 형식 통과)"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage7-post-pr-verify.sh <issue> — 7단계(PR 생성) post-execution 게이트
|
|
3
|
+
#
|
|
4
|
+
# 원칙: entry gate 는 전제조건, post-verify 는 완료 조건. publisher 가 §2-5 마지막에
|
|
5
|
+
# .done=true 를 기록하기 직전 본 스크립트를 호출한다. 실패 시 exit 2 로 publisher
|
|
6
|
+
# 재작업 loop 를 유도한다.
|
|
7
|
+
#
|
|
8
|
+
# 검사 항목 (완료조건):
|
|
9
|
+
# 1) origin/<current-branch> 원격 브랜치 존재 (git push 성공 여부)
|
|
10
|
+
# 2) .stages.3_delivery.substages.pr.draft_url 이 HTTPS URL 문자열
|
|
11
|
+
# 3) .stages.3_delivery.substages.pr.body_validation 3항목 모두 true
|
|
12
|
+
# 4) reviewer 마커 (reviewer_added=true XOR reviewer_self_skipped=true; 상호 배타)
|
|
13
|
+
# 5) .stages.3_delivery.substages.pr.done == true (publisher 자기선언)
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
ISSUE="${1:?usage: stage7-post-pr-verify.sh <issue>}"
|
|
17
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
18
|
+
fail(){ echo "MAKDOONG2-POSTGATE BLOCKED [3_delivery.pr_post]: $1" >&2; exit 2; }
|
|
19
|
+
[ -f "$P" ] || fail "state.json 없음"
|
|
20
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
21
|
+
|
|
22
|
+
WT="$(q '.worktree')"
|
|
23
|
+
[ -d "$WT" ] || fail "worktree 경로 없음: $WT"
|
|
24
|
+
|
|
25
|
+
BR="$(git -C "$WT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')"
|
|
26
|
+
[ -n "$BR" ] || fail "현재 브랜치 확인 불가"
|
|
27
|
+
git -C "$WT" rev-parse --verify "origin/$BR" >/dev/null 2>&1 \
|
|
28
|
+
|| fail "remote 에 push 되지 않음 (origin/$BR 없음) — publisher 가 §2-4 에서 git push -u origin HEAD 를 실행했는지 확인"
|
|
29
|
+
|
|
30
|
+
URL="$(q '.stages."3_delivery".substages."pr".draft_url')"
|
|
31
|
+
case "$URL" in
|
|
32
|
+
http://*|https://*) : ;;
|
|
33
|
+
*) fail "draft_url 이 유효한 HTTPS URL 문자열 아님: $URL" ;;
|
|
34
|
+
esac
|
|
35
|
+
|
|
36
|
+
for k in no_orphan_scenarios template_match section_content_match; do
|
|
37
|
+
v="$(q ".stages.\"3_delivery\".substages.\"pr\".body_validation.$k")"
|
|
38
|
+
[ "$v" = "true" ] || fail "body_validation.$k=$v — 3항목 모두 true 여야 함"
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
RA="$(q '.stages."3_delivery".substages."pr".reviewer_added')"
|
|
42
|
+
RS="$(q '.stages."3_delivery".substages."pr".reviewer_self_skipped')"
|
|
43
|
+
if [ "$RA" = "true" ] && [ "$RS" = "true" ]; then
|
|
44
|
+
fail "reviewer 마커 상호 배타 위반 — reviewer_added 와 reviewer_self_skipped 가 동시에 true"
|
|
45
|
+
fi
|
|
46
|
+
if [ "$RA" != "true" ] && [ "$RS" != "true" ]; then
|
|
47
|
+
fail "reviewer 마커 미기록 — reviewer_added=true 또는 reviewer_self_skipped=true 중 하나 필수"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
D="$(q '.stages."3_delivery".substages."pr".done')"
|
|
51
|
+
[ "$D" = "true" ] || fail "pr.done 이 true 아님(=$D) — publisher 가 §2-5 완료 마킹을 누락"
|
|
52
|
+
|
|
53
|
+
echo "MAKDOONG2-POSTGATE OK: 3_delivery.pr_post"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stage7-pr-verify.sh <issue> — 7단계(PR 생성) 진입 게이트
|
|
3
|
+
#
|
|
4
|
+
# 원칙: entry gate 는 substage 진입 "전제조건" 만 검사한다.
|
|
5
|
+
# 완료 조건(원격 push 여부, PR URL 존재, reviewer 등)은 publisher 가 substage 수행 후
|
|
6
|
+
# stage7-post-pr-verify.sh + verifier 에서 검증된다.
|
|
7
|
+
#
|
|
8
|
+
# 검사 항목 (전제조건):
|
|
9
|
+
# 1) state.json 존재
|
|
10
|
+
# 2) 3_delivery.commit substage 완료
|
|
11
|
+
# 3) worktree 가 메인 repo 의 형제 디렉토리로 존재
|
|
12
|
+
# 4) worktree 에 uncommitted 변경 없음 (tracked 파일 기준; untracked 는 무시)
|
|
13
|
+
# 5) 현재 브랜치 확인 가능
|
|
14
|
+
#
|
|
15
|
+
# 제거된 조건 (완료조건 → post-verify 로 이동):
|
|
16
|
+
# * origin/<BR> 원격 브랜치 존재 여부 — publisher 가 §2-4 에서 git push 를 수행하므로
|
|
17
|
+
# first-entry 시점에는 존재할 수 없음. chicken-and-egg 방지를 위해 post-verify 로 이관.
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
ISSUE="${1:?usage: stage7-pr-verify.sh <issue>}"
|
|
21
|
+
P="$("$HERE/../scripts/state.sh" root)/.makdoong2-team/$ISSUE/state.json"
|
|
22
|
+
fail(){ echo "MAKDOONG2-GATE BLOCKED [3_delivery.pr]: $1" >&2; exit 2; }
|
|
23
|
+
[ -f "$P" ] || fail "state.json 없음 — planning phase부터 시작하라"
|
|
24
|
+
q(){ "$HERE/../scripts/state.sh" get "$ISSUE" "$1" 2>/dev/null || echo "__MISSING__"; }
|
|
25
|
+
assert_worktree_sibling(){
|
|
26
|
+
local wt="$1" main pwt pmain
|
|
27
|
+
[ -d "$wt" ] || fail "worktree 경로가 존재하지 않음: $wt"
|
|
28
|
+
main="$(git -C "$wt" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
|
|
29
|
+
[ -n "$main" ] || fail "메인 repo 식별 실패"
|
|
30
|
+
[ "$wt" != "$main" ] || fail "worktree가 메인 repo와 동일 경로 — 별도 worktree 필요"
|
|
31
|
+
pwt="$(cd "$(dirname "$wt")" && pwd -P)"
|
|
32
|
+
pmain="$(cd "$(dirname "$main")" && pwd -P)"
|
|
33
|
+
[ "$pwt" = "$pmain" ] || fail "worktree가 메인 repo의 형제 디렉토리가 아님: $wt (메인: $main)"
|
|
34
|
+
}
|
|
35
|
+
[ "$(q '.stages."3_delivery".substages."commit".done')" = "true" ] || fail "commit substage 미완료"
|
|
36
|
+
WT="$(q '.worktree')"
|
|
37
|
+
assert_worktree_sibling "$WT"
|
|
38
|
+
|
|
39
|
+
MODIFIED="$(git -C "$WT" status --porcelain | grep -v '^?? ' || true)"
|
|
40
|
+
|
|
41
|
+
if [ -n "$MODIFIED" ]; then
|
|
42
|
+
fail "worktree에 uncommitted 변경(M/A/D/R)이 있음 — 커밋하거나 제거 필요"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
BR="$(git -C "$WT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')"
|
|
46
|
+
[ -n "$BR" ] || fail "현재 브랜치 확인 불가"
|
|
47
|
+
|
|
48
|
+
echo "MAKDOONG2-GATE OK: 3_delivery.pr"
|