moai-adk 0.9.1__py3-none-any.whl → 0.10.1__py3-none-any.whl
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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/templates/.claude/hooks/alfred/core/project.py +750 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +97 -24
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +4 -0
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +140 -30
- moai_adk/templates/CLAUDE.md +58 -0
- moai_adk/templates/README.md +256 -0
- {moai_adk-0.9.1.dist-info → moai_adk-0.10.1.dist-info}/METADATA +714 -79
- {moai_adk-0.9.1.dist-info → moai_adk-0.10.1.dist-info}/RECORD +12 -23
- moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +0 -9
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -343
- moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +0 -471
- moai_adk/templates/.github/workflows/tag-report.yml +0 -261
- moai_adk/templates/.moai/docs/quick-issue-creation-guide.md +0 -219
- moai_adk/templates/.moai/hooks/install.sh +0 -79
- moai_adk/templates/.moai/hooks/pre-commit.sh +0 -66
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/src/moai_adk/core/__init__.py +0 -5
- moai_adk/templates/src/moai_adk/core/tags/__init__.py +0 -86
- moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +0 -433
- moai_adk/templates/src/moai_adk/core/tags/cli.py +0 -283
- moai_adk/templates/src/moai_adk/core/tags/validator.py +0 -897
- {moai_adk-0.9.1.dist-info → moai_adk-0.10.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.1.dist-info → moai_adk-0.10.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.9.1.dist-info → moai_adk-0.10.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,6 +16,58 @@ from typing import Any
|
|
|
16
16
|
CACHE_DIR_NAME = ".moai/cache"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def find_project_root(start_path: str | Path = ".") -> Path:
|
|
20
|
+
"""Find MoAI-ADK project root by searching upward for .moai/config.json
|
|
21
|
+
|
|
22
|
+
Traverses up the directory tree until it finds .moai/config.json or CLAUDE.md,
|
|
23
|
+
which indicates the project root. This ensures cache and other files are
|
|
24
|
+
always created in the correct location, regardless of where hooks execute.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
start_path: Starting directory (default: current directory)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Project root Path. If not found, returns start_path as absolute path.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> find_project_root(".")
|
|
34
|
+
Path("/Users/user/my-project")
|
|
35
|
+
>>> find_project_root(".claude/hooks/alfred")
|
|
36
|
+
Path("/Users/user/my-project") # Found root 3 levels up
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
39
|
+
- Searches for .moai/config.json first (most reliable)
|
|
40
|
+
- Falls back to CLAUDE.md if config.json not found
|
|
41
|
+
- Max depth: 10 levels up (prevent infinite loop)
|
|
42
|
+
- Returns absolute path for consistency
|
|
43
|
+
|
|
44
|
+
TDD History:
|
|
45
|
+
- RED: 4 test scenarios (root, nested, not found, symlinks)
|
|
46
|
+
- GREEN: Minimal upward search with .moai/config.json detection
|
|
47
|
+
- REFACTOR: Add CLAUDE.md fallback, max depth limit, absolute path return
|
|
48
|
+
"""
|
|
49
|
+
current = Path(start_path).resolve()
|
|
50
|
+
max_depth = 10 # Prevent infinite loop
|
|
51
|
+
|
|
52
|
+
for _ in range(max_depth):
|
|
53
|
+
# Check for .moai/config.json (primary indicator)
|
|
54
|
+
if (current / ".moai" / "config.json").exists():
|
|
55
|
+
return current
|
|
56
|
+
|
|
57
|
+
# Check for CLAUDE.md (secondary indicator)
|
|
58
|
+
if (current / "CLAUDE.md").exists():
|
|
59
|
+
return current
|
|
60
|
+
|
|
61
|
+
# Move up one level
|
|
62
|
+
parent = current.parent
|
|
63
|
+
if parent == current: # Reached filesystem root
|
|
64
|
+
break
|
|
65
|
+
current = parent
|
|
66
|
+
|
|
67
|
+
# Not found - return start_path as absolute
|
|
68
|
+
return Path(start_path).resolve()
|
|
69
|
+
|
|
70
|
+
|
|
19
71
|
class TimeoutError(Exception):
|
|
20
72
|
"""Signal-based timeout exception"""
|
|
21
73
|
pass
|
|
@@ -246,7 +298,7 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
246
298
|
Counts the number of SPECs with status: completed.
|
|
247
299
|
|
|
248
300
|
Args:
|
|
249
|
-
cwd: Project root directory path
|
|
301
|
+
cwd: Project root directory path (or any subdirectory, will search upward)
|
|
250
302
|
|
|
251
303
|
Returns:
|
|
252
304
|
SPEC progress dictionary. Includes the following keys:
|
|
@@ -264,15 +316,19 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
264
316
|
|
|
265
317
|
Notes:
|
|
266
318
|
- SPEC File Location: .moai/specs/SPEC-{ID}/spec.md
|
|
267
|
-
- Completion condition: Include
|
|
319
|
+
- Completion condition: Include "status: completed" in YAML front matter
|
|
268
320
|
- If parsing fails, the SPEC is considered incomplete.
|
|
321
|
+
- Automatically finds project root to locate .moai/specs/
|
|
269
322
|
|
|
270
323
|
TDD History:
|
|
271
324
|
- RED: 5 items scenario test (0/0, 2/5, 5/5, no directory, parsing error)
|
|
272
325
|
- GREEN: SPEC search with Path.iterdir(), YAML parsing implementation
|
|
273
326
|
- REFACTOR: Strengthened exception handling, improved percentage calculation safety
|
|
327
|
+
- UPDATE: Add project root detection for consistent path resolution
|
|
274
328
|
"""
|
|
275
|
-
|
|
329
|
+
# Find project root to ensure we read specs from correct location
|
|
330
|
+
project_root = find_project_root(cwd)
|
|
331
|
+
specs_dir = project_root / ".moai" / "specs"
|
|
276
332
|
|
|
277
333
|
if not specs_dir.exists():
|
|
278
334
|
return {"completed": 0, "total": 0, "percentage": 0}
|
|
@@ -316,7 +372,7 @@ def get_project_language(cwd: str) -> str:
|
|
|
316
372
|
"""Determine the primary project language (prefers config.json).
|
|
317
373
|
|
|
318
374
|
Args:
|
|
319
|
-
cwd: Project root directory.
|
|
375
|
+
cwd: Project root directory (or any subdirectory, will search upward).
|
|
320
376
|
|
|
321
377
|
Returns:
|
|
322
378
|
Language string in lower-case.
|
|
@@ -324,8 +380,11 @@ def get_project_language(cwd: str) -> str:
|
|
|
324
380
|
Notes:
|
|
325
381
|
- Reads ``.moai/config.json`` first for a quick answer.
|
|
326
382
|
- Falls back to ``detect_language`` if configuration is missing.
|
|
383
|
+
- Automatically finds project root to locate .moai/config.json
|
|
327
384
|
"""
|
|
328
|
-
|
|
385
|
+
# Find project root to ensure we read config from correct location
|
|
386
|
+
project_root = find_project_root(cwd)
|
|
387
|
+
config_path = project_root / ".moai" / "config.json"
|
|
329
388
|
if config_path.exists():
|
|
330
389
|
try:
|
|
331
390
|
config = json.loads(config_path.read_text())
|
|
@@ -336,8 +395,8 @@ def get_project_language(cwd: str) -> str:
|
|
|
336
395
|
# Fall back to detection on parse errors
|
|
337
396
|
pass
|
|
338
397
|
|
|
339
|
-
# Fall back to the original language detection routine
|
|
340
|
-
return detect_language(
|
|
398
|
+
# Fall back to the original language detection routine (use project root)
|
|
399
|
+
return detect_language(str(project_root))
|
|
341
400
|
|
|
342
401
|
|
|
343
402
|
# @CODE:CONFIG-INTEGRATION-001
|
|
@@ -382,7 +441,9 @@ def get_version_check_config(cwd: str) -> dict[str, Any]:
|
|
|
382
441
|
"cache_ttl_hours": 24
|
|
383
442
|
}
|
|
384
443
|
|
|
385
|
-
|
|
444
|
+
# Find project root to ensure we read config from correct location
|
|
445
|
+
project_root = find_project_root(cwd)
|
|
446
|
+
config_path = project_root / ".moai" / "config.json"
|
|
386
447
|
if not config_path.exists():
|
|
387
448
|
return defaults
|
|
388
449
|
|
|
@@ -509,36 +570,42 @@ def is_major_version_change(current: str, latest: str) -> bool:
|
|
|
509
570
|
def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
510
571
|
"""Check MoAI-ADK current and latest version with caching and offline support.
|
|
511
572
|
|
|
573
|
+
⭐ CRITICAL GUARANTEE: This function ALWAYS returns the current installed version.
|
|
574
|
+
Network failures, cache issues, and timeouts NEVER result in "unknown" version.
|
|
575
|
+
|
|
512
576
|
Execution flow:
|
|
513
|
-
1.
|
|
514
|
-
2.
|
|
515
|
-
3.
|
|
516
|
-
4. If
|
|
517
|
-
5.
|
|
577
|
+
1. Get current installed version (ALWAYS succeeds) ← CRITICAL
|
|
578
|
+
2. Build minimal result with current version
|
|
579
|
+
3. Try to load from cache (< 50ms) - optional enhancement
|
|
580
|
+
4. If cache valid, return cached latest info
|
|
581
|
+
5. If cache invalid/miss, optionally query PyPI - optional enhancement
|
|
582
|
+
6. Save result to cache for next time - optional
|
|
518
583
|
|
|
519
584
|
Args:
|
|
520
585
|
cwd: Project root directory (for cache location)
|
|
521
586
|
|
|
522
587
|
Returns:
|
|
523
588
|
dict with keys:
|
|
524
|
-
- "current": Current installed version
|
|
525
|
-
- "latest": Latest version available on PyPI
|
|
589
|
+
- "current": Current installed version (ALWAYS valid, never empty)
|
|
590
|
+
- "latest": Latest version available on PyPI (may be "unknown")
|
|
526
591
|
- "update_available": Boolean indicating if update is available
|
|
527
592
|
- "upgrade_command": Recommended upgrade command (if update available)
|
|
528
|
-
- "release_notes_url": URL to release notes
|
|
529
|
-
- "is_major_update": Boolean indicating major version change
|
|
593
|
+
- "release_notes_url": URL to release notes
|
|
594
|
+
- "is_major_update": Boolean indicating major version change
|
|
530
595
|
|
|
531
|
-
|
|
532
|
-
- Cache hit (< 24 hours): Returns in ~20ms, no network access
|
|
533
|
-
- Cache miss + online: Query PyPI (1s timeout), cache result
|
|
534
|
-
- Cache miss + offline: Return current version only (~100ms)
|
|
535
|
-
-
|
|
596
|
+
Guarantees:
|
|
597
|
+
- Cache hit (< 24 hours): Returns in ~20ms, no network access ✓
|
|
598
|
+
- Cache miss + online: Query PyPI (1s timeout), cache result ✓
|
|
599
|
+
- Cache miss + offline: Return current version only (~100ms) ✓
|
|
600
|
+
- Network timeout: Returns current + "unknown" latest (~50ms) ✓
|
|
601
|
+
- Any exception: Always returns current version ✓
|
|
536
602
|
|
|
537
603
|
TDD History:
|
|
538
604
|
- RED: 5 test scenarios (network detection, cache integration, offline mode)
|
|
539
605
|
- GREEN: Integrate VersionCache with network detection
|
|
540
606
|
- REFACTOR: Extract cache directory constant, improve error handling
|
|
541
607
|
- Phase 3: Add release_notes_url and is_major_update fields (@CODE:VERSION-INTEGRATE-FIELDS-001)
|
|
608
|
+
- Phase 4: CRITICAL FIX - Always guarantee current version return (@CODE:VERSION-ALWAYS-VALID-001)
|
|
542
609
|
"""
|
|
543
610
|
import importlib.util
|
|
544
611
|
import urllib.error
|
|
@@ -560,8 +627,13 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
560
627
|
# Graceful degradation: skip caching on import errors
|
|
561
628
|
VersionCache = None
|
|
562
629
|
|
|
563
|
-
# 1.
|
|
564
|
-
|
|
630
|
+
# 1. Find project root (ensure cache is always in correct location)
|
|
631
|
+
# This prevents creating .moai/cache in wrong locations when hooks run
|
|
632
|
+
# from subdirectories like .claude/hooks/alfred/
|
|
633
|
+
project_root = find_project_root(cwd)
|
|
634
|
+
|
|
635
|
+
# 2. Initialize cache (skip if VersionCache couldn't be imported)
|
|
636
|
+
cache_dir = project_root / CACHE_DIR_NAME
|
|
565
637
|
version_cache = VersionCache(cache_dir) if VersionCache else None
|
|
566
638
|
|
|
567
639
|
# 2. Get current installed version first (needed for cache validation)
|
|
@@ -672,6 +744,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
672
744
|
|
|
673
745
|
|
|
674
746
|
__all__ = [
|
|
747
|
+
"find_project_root",
|
|
675
748
|
"detect_language",
|
|
676
749
|
"get_git_info",
|
|
677
750
|
"count_specs",
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# MoAI-ADK GitFlow Main Branch Control Hook
|
|
4
|
+
# Purpose: Enforce GitFlow in team mode, advisory in personal mode
|
|
5
|
+
# Enforces: Strict team workflow, flexible personal development
|
|
6
|
+
#
|
|
7
|
+
# This hook runs before any git push operation:
|
|
8
|
+
# Team Mode: Blocks direct main/master push (non-develop), requires confirmation for develop→main
|
|
9
|
+
# Personal Mode: Advisory warnings, allows flexibility
|
|
10
|
+
#
|
|
11
|
+
# Exit codes:
|
|
12
|
+
# 0 - Push allowed
|
|
13
|
+
# 1 - Push blocked (team mode violation or user declined)
|
|
14
|
+
|
|
15
|
+
# Check team mode from .moai/config.json
|
|
16
|
+
is_team_mode() {
|
|
17
|
+
if [ -f ".moai/config.json" ]; then
|
|
18
|
+
# Check if mode is "team"
|
|
19
|
+
grep -q '"mode".*:.*"team"' ".moai/config.json" 2>/dev/null
|
|
20
|
+
return $?
|
|
21
|
+
fi
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Colors for output
|
|
26
|
+
RED='\033[0;31m'
|
|
27
|
+
YELLOW='\033[1;33m'
|
|
28
|
+
GREEN='\033[0;32m'
|
|
29
|
+
BLUE='\033[0;34m'
|
|
30
|
+
NC='\033[0m' # No Color
|
|
31
|
+
|
|
32
|
+
TEAM_MODE=false
|
|
33
|
+
is_team_mode && TEAM_MODE=true
|
|
34
|
+
|
|
35
|
+
# Read from stdin (git sends remote, local ref info)
|
|
36
|
+
# Format: <local ref> <local oid> <remote ref> <remote oid>
|
|
37
|
+
while read local_ref local_oid remote_ref remote_oid; do
|
|
38
|
+
# Extract the remote branch name from the reference
|
|
39
|
+
# remote_ref format: refs/heads/main
|
|
40
|
+
remote_branch=$(echo "$remote_ref" | sed 's|refs/heads/||')
|
|
41
|
+
local_branch=$(echo "$local_ref" | sed 's|refs/heads/||')
|
|
42
|
+
|
|
43
|
+
# Check if attempting to push to main branch
|
|
44
|
+
if [ "$remote_branch" = "main" ] || [ "$remote_branch" = "master" ]; then
|
|
45
|
+
# Get the current branch to determine if this is the develop branch
|
|
46
|
+
current_branch=$(git rev-parse --abbrev-ref HEAD)
|
|
47
|
+
|
|
48
|
+
# TEAM MODE ENFORCEMENT
|
|
49
|
+
if [ "$TEAM_MODE" = true ]; then
|
|
50
|
+
# Block non-develop, non-release branches from pushing to main
|
|
51
|
+
if [ "$local_branch" != "develop" ] && [ "${local_branch#release/}" = "$local_branch" ]; then
|
|
52
|
+
echo ""
|
|
53
|
+
echo -e "${RED}❌ BLOCKED: Non-standard GitFlow in TEAM MODE${NC}"
|
|
54
|
+
echo ""
|
|
55
|
+
echo -e "${BLUE}Current branch: ${local_branch}${NC}"
|
|
56
|
+
echo -e "${BLUE}Target branch: ${remote_branch}${NC}"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "🚀 Correct GitFlow workflow for TEAM MODE:"
|
|
59
|
+
echo " 1. Work on feature/SPEC-{ID} branch (created from develop)"
|
|
60
|
+
echo " 2. Push to feature/SPEC-{ID} and create PR to develop"
|
|
61
|
+
echo " 3. Code review & merge into develop"
|
|
62
|
+
echo " 4. When develop is stable, create PR from develop to main"
|
|
63
|
+
echo " 5. Release manager merges develop → main with tag"
|
|
64
|
+
echo ""
|
|
65
|
+
echo -e "${RED}⚠️ Push to ${remote_branch} blocked in team mode${NC}"
|
|
66
|
+
echo ""
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# For develop → main or release/* → main, ask for confirmation
|
|
71
|
+
if [ "$local_branch" = "develop" ] || [ "${local_branch#release/}" != "$local_branch" ]; then
|
|
72
|
+
echo ""
|
|
73
|
+
echo -e "${YELLOW}⚠️ TEAM MODE: Pushing ${local_branch} → ${remote_branch}${NC}"
|
|
74
|
+
echo ""
|
|
75
|
+
echo "📋 Summary:"
|
|
76
|
+
echo " • Source branch: ${local_branch}"
|
|
77
|
+
echo " • Target branch: ${remote_branch}"
|
|
78
|
+
echo " • Mode: TEAM MODE (strict enforcement)"
|
|
79
|
+
echo ""
|
|
80
|
+
read -p "❓ Are you sure you want to push ${local_branch} to ${remote_branch}? (y/n) " -n 1 -r
|
|
81
|
+
echo ""
|
|
82
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
83
|
+
echo -e "${RED}✓ Push cancelled by user${NC}"
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# PERSONAL MODE: Advisory warnings (allow all pushes)
|
|
90
|
+
if [ "$TEAM_MODE" = false ]; then
|
|
91
|
+
# Advisory: recommend develop -> main workflow
|
|
92
|
+
if [ "$local_branch" != "develop" ] && [ "${local_branch#release/}" = "$local_branch" ]; then
|
|
93
|
+
echo ""
|
|
94
|
+
echo -e "${YELLOW}⚠️ ADVISORY: Non-standard GitFlow detected${NC}"
|
|
95
|
+
echo ""
|
|
96
|
+
echo -e "${BLUE}Current branch: ${local_branch}${NC}"
|
|
97
|
+
echo -e "${BLUE}Target branch: ${remote_branch}${NC}"
|
|
98
|
+
echo ""
|
|
99
|
+
echo "Recommended GitFlow workflow:"
|
|
100
|
+
echo " 1. Work on feature/SPEC-{ID} branch (created from develop)"
|
|
101
|
+
echo " 2. Push to feature/SPEC-{ID} and create PR to develop"
|
|
102
|
+
echo " 3. Merge into develop after code review"
|
|
103
|
+
echo " 4. When develop is stable, create PR from develop to main"
|
|
104
|
+
echo " 5. Release manager merges develop -> main with tag"
|
|
105
|
+
echo ""
|
|
106
|
+
echo -e "${GREEN}✓ Push will proceed (personal mode - flexibility enabled)${NC}"
|
|
107
|
+
echo ""
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Check for delete operation
|
|
111
|
+
if [ "$local_oid" = "0000000000000000000000000000000000000000" ]; then
|
|
112
|
+
echo ""
|
|
113
|
+
echo -e "${RED}⚠️ WARNING: Attempting to delete main branch${NC}"
|
|
114
|
+
echo ""
|
|
115
|
+
echo -e "${YELLOW}This operation is highly discouraged.${NC}"
|
|
116
|
+
echo -e "${GREEN}✓ Push will proceed (personal mode - flexibility enabled)${NC}"
|
|
117
|
+
echo ""
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Check for force push attempts to main
|
|
121
|
+
if [ "$remote_branch" = "main" ] || [ "$remote_branch" = "master" ]; then
|
|
122
|
+
# Check if remote_oid exists (non-zero means we're trying to update existing ref)
|
|
123
|
+
if [ "$remote_oid" != "0000000000000000000000000000000000000000" ]; then
|
|
124
|
+
# Verify this is a fast-forward merge (no force push)
|
|
125
|
+
if ! git merge-base --is-ancestor "$remote_oid" "$local_oid" 2>/dev/null; then
|
|
126
|
+
echo ""
|
|
127
|
+
echo -e "${YELLOW}⚠️ ADVISORY: Force-push to main branch detected${NC}"
|
|
128
|
+
echo ""
|
|
129
|
+
echo "Recommended approach:"
|
|
130
|
+
echo " - Use GitHub PR with proper code review"
|
|
131
|
+
echo " - Ensure changes are merged via fast-forward"
|
|
132
|
+
echo ""
|
|
133
|
+
echo -e "${GREEN}✓ Push will proceed (personal mode - flexibility enabled)${NC}"
|
|
134
|
+
echo ""
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
done
|
|
141
|
+
|
|
142
|
+
# All checks passed (or advisory warnings shown)
|
|
143
|
+
exit 0
|
|
@@ -17,6 +17,10 @@ jobs:
|
|
|
17
17
|
# Skip validation on draft PRs (allow WIP)
|
|
18
18
|
if: github.event.pull_request.draft == false || github.event_name == 'push'
|
|
19
19
|
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
pull-requests: write
|
|
23
|
+
|
|
20
24
|
steps:
|
|
21
25
|
- name: Checkout code
|
|
22
26
|
uses: actions/checkout@v4
|
|
@@ -1,36 +1,40 @@
|
|
|
1
|
-
# GitFlow
|
|
1
|
+
# GitFlow Protection Policy
|
|
2
2
|
|
|
3
|
-
**Document ID**: @DOC:GITFLOW-POLICY-
|
|
4
|
-
**Published**: 2025-10-17
|
|
5
|
-
**
|
|
3
|
+
**Document ID**: @DOC:GITFLOW-POLICY-ALIAS
|
|
4
|
+
**Published**: 2025-10-17
|
|
5
|
+
**Updated**: 2025-10-29
|
|
6
|
+
**Status**: **Enforced via GitHub Branch Protection** (v0.8.3+)
|
|
6
7
|
**Scope**: Personal and Team modes
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
## Overview
|
|
11
12
|
|
|
12
|
-
MoAI-ADK **
|
|
13
|
+
MoAI-ADK **enforces** a GitFlow-inspired workflow through GitHub Branch Protection. As of v0.8.3, the `main` branch is protected and requires Pull Requests for all changes, including from administrators.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
**What Changed**: Previously (v0.3.5-v0.8.2), we used an advisory approach with warnings. Now we enforce proper GitFlow to ensure code quality and prevent accidental direct pushes to main.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
## Key Requirements (Enforced)
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|----------------|---------|-------------|
|
|
20
|
-
| **Merge via develop** | Prefer merging `develop` into `main` | Advisory ⚠️ |
|
|
21
|
-
| **Feature branches off develop** | Branch from `develop` and raise PRs back to `develop` | Advisory ⚠️ |
|
|
22
|
-
| **Release process** | Release flow: `develop` → `main` (release engineer encouraged) | Advisory ⚠️ |
|
|
23
|
-
| **Force push** | Warn when force-pushing, but allow it | Warning ⚠️ |
|
|
24
|
-
| **Direct push** | Warn on direct pushes to `main`, but allow them | Warning ⚠️ |
|
|
19
|
+
### 1. Main Branch Access (Enforced)
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
| Requirement | Summary | Enforcement |
|
|
22
|
+
|-------------|---------|-------------|
|
|
23
|
+
| **Merge via develop** | MUST merge `develop` into `main` | ✅ Enforced |
|
|
24
|
+
| **Feature branches off develop** | MUST branch from `develop` and raise PRs back to `develop` | ✅ Enforced |
|
|
25
|
+
| **Release process** | Release flow: `develop` → `main` (PR required) | ✅ Enforced |
|
|
26
|
+
| **Force push** | Blocked on `main` | ✅ Blocked |
|
|
27
|
+
| **Direct push** | Blocked on `main` (PR required) | ✅ Blocked |
|
|
28
|
+
|
|
29
|
+
### 2. Git Workflow (Required)
|
|
27
30
|
|
|
28
31
|
```
|
|
29
32
|
┌─────────────────────────────────────────────────────────┐
|
|
30
|
-
│
|
|
33
|
+
│ ENFORCED GITFLOW │
|
|
34
|
+
│ (GitHub Branch Protection Active) │
|
|
31
35
|
└─────────────────────────────────────────────────────────┘
|
|
32
36
|
|
|
33
|
-
develop (
|
|
37
|
+
develop (required base branch)
|
|
34
38
|
↑ ↓
|
|
35
39
|
┌─────────────────┐
|
|
36
40
|
│ │
|
|
@@ -46,13 +50,15 @@ feature/SPEC-{ID} [PR: feature -> develop]
|
|
|
46
50
|
│ (release manager prepares)
|
|
47
51
|
↓
|
|
48
52
|
[PR: develop -> main]
|
|
53
|
+
[Code review + approval REQUIRED]
|
|
54
|
+
[All discussions resolved]
|
|
49
55
|
[CI/CD validation]
|
|
50
56
|
[tag creation]
|
|
51
57
|
↓
|
|
52
|
-
main (release)
|
|
58
|
+
main (protected release)
|
|
53
59
|
```
|
|
54
60
|
|
|
55
|
-
**
|
|
61
|
+
**Enforcement**: Direct pushes to `main` are **blocked** via GitHub Branch Protection. All changes must go through Pull Requests.
|
|
56
62
|
|
|
57
63
|
## Technical Implementation
|
|
58
64
|
|
|
@@ -156,18 +162,29 @@ git push origin v1.0.0
|
|
|
156
162
|
|
|
157
163
|
## Policy Modes
|
|
158
164
|
|
|
159
|
-
### Strict Mode (
|
|
165
|
+
### Strict Mode (Active, v0.8.3+) ✅ ENFORCED
|
|
166
|
+
|
|
167
|
+
**GitHub Branch Protection Enabled**:
|
|
168
|
+
- ✅ **enforce_admins: true** - Administrators must follow all rules
|
|
169
|
+
- ✅ **required_pull_request_reviews** - 1 approval required
|
|
170
|
+
- ✅ **required_conversation_resolution** - All discussions must be resolved
|
|
171
|
+
- ✅ **Block direct pushes to `main`** - PR required for all users
|
|
172
|
+
- ✅ **Block force pushes** - Prevents history rewriting
|
|
173
|
+
- ✅ **Block branch deletion** - Protects main from accidental deletion
|
|
160
174
|
|
|
161
|
-
|
|
162
|
-
- ❌
|
|
163
|
-
-
|
|
175
|
+
**What This Means**:
|
|
176
|
+
- ❌ No one (including admins) can push directly to `main`
|
|
177
|
+
- ✅ All changes must go through Pull Requests
|
|
178
|
+
- ✅ PRs require code review approval
|
|
179
|
+
- ✅ All code discussions must be resolved before merge
|
|
180
|
+
- ✅ Enforces proper GitFlow: feature → develop → main
|
|
164
181
|
|
|
165
|
-
### Advisory Mode (
|
|
182
|
+
### Advisory Mode (Legacy, v0.3.5 - v0.8.2)
|
|
166
183
|
|
|
167
|
-
- ⚠️
|
|
168
|
-
- ⚠️
|
|
169
|
-
- ⚠️
|
|
170
|
-
-
|
|
184
|
+
- ⚠️ Warned but allowed direct pushes to `main`
|
|
185
|
+
- ⚠️ Warned but allowed force pushes
|
|
186
|
+
- ⚠️ Recommended best practices while preserving flexibility
|
|
187
|
+
- ❌ **Deprecated** - Replaced by Strict Mode for better quality control
|
|
171
188
|
|
|
172
189
|
---
|
|
173
190
|
|
|
@@ -202,8 +219,98 @@ A: Yes. Expect an advisory warning, yet the push continues.
|
|
|
202
219
|
**Q: Can I disable the hook entirely?**
|
|
203
220
|
A: Yes. Remove `.git/hooks/pre-push` or strip its execute permission.
|
|
204
221
|
|
|
205
|
-
**Q: Why switch to Advisory Mode?**
|
|
206
|
-
A:
|
|
222
|
+
**Q: Why switch to Advisory Mode?**
|
|
223
|
+
A: Advisory Mode was used in v0.3.5-v0.8.2. As of v0.8.3, we've switched to Strict Mode with GitHub Branch Protection for better quality control.
|
|
224
|
+
|
|
225
|
+
**Q: What if develop falls behind main?**
|
|
226
|
+
A: This can happen when hotfixes or releases go directly to main. Regularly sync main → develop to prevent divergence. See "Maintaining develop-main Sync" section below.
|
|
227
|
+
|
|
228
|
+
**Q: Can I bypass branch protection in emergencies?**
|
|
229
|
+
A: No. Even administrators must follow the PR process. For true emergencies, temporarily disable protection via GitHub Settings (requires admin access), but re-enable immediately after.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Maintaining develop-main Sync
|
|
234
|
+
|
|
235
|
+
### ⚠️ Critical Rule: develop Must Stay Current
|
|
236
|
+
|
|
237
|
+
**Problem**: When main receives direct commits (hotfixes, emergency releases) without syncing back to develop, GitFlow breaks:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
❌ BAD STATE:
|
|
241
|
+
develop: 3 commits ahead, 29 commits behind main
|
|
242
|
+
- develop has outdated dependencies
|
|
243
|
+
- New features branch from old code
|
|
244
|
+
- Merge conflicts multiply over time
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Signs of Drift
|
|
248
|
+
|
|
249
|
+
Monitor for these warnings:
|
|
250
|
+
- `git status` shows "Your branch is X commits behind main"
|
|
251
|
+
- Feature branches conflict with main during PR
|
|
252
|
+
- CI/CD failures due to dependency mismatches
|
|
253
|
+
- Version numbers in develop don't match main
|
|
254
|
+
|
|
255
|
+
### Recovery Procedure
|
|
256
|
+
|
|
257
|
+
When develop falls behind main:
|
|
258
|
+
|
|
259
|
+
1. **Assess the Gap**
|
|
260
|
+
```bash
|
|
261
|
+
git log --oneline develop..main # Commits in main but not develop
|
|
262
|
+
git log --oneline main..develop # Commits in develop but not main
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
2. **Sync Strategy: Merge main into develop (Recommended)**
|
|
266
|
+
```bash
|
|
267
|
+
git checkout develop
|
|
268
|
+
git pull origin develop # Get latest develop
|
|
269
|
+
git merge main # Merge main into develop
|
|
270
|
+
# Resolve conflicts if any (prefer main for version/config files)
|
|
271
|
+
git push origin develop
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
3. **Emergency Only: Reset develop to main (Destructive)**
|
|
275
|
+
```bash
|
|
276
|
+
# ⚠️ ONLY if develop's unique commits are unwanted
|
|
277
|
+
git checkout develop
|
|
278
|
+
git reset --hard main
|
|
279
|
+
git push origin develop --force
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Prevention: Regular Sync Schedule
|
|
283
|
+
|
|
284
|
+
**After every main release** (REQUIRED):
|
|
285
|
+
```bash
|
|
286
|
+
# Immediately after merging develop → main:
|
|
287
|
+
git checkout develop
|
|
288
|
+
git merge main
|
|
289
|
+
git push origin develop
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Weekly maintenance** (for active projects):
|
|
293
|
+
```bash
|
|
294
|
+
# Every Monday morning:
|
|
295
|
+
git checkout develop
|
|
296
|
+
git pull origin main
|
|
297
|
+
git push origin develop
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Real-World Case Study (2025-10-29)
|
|
301
|
+
|
|
302
|
+
**Situation**: develop was 29 commits behind main due to:
|
|
303
|
+
- v0.8.2, v0.8.3 released directly to main
|
|
304
|
+
- No reverse sync to develop
|
|
305
|
+
- Feature branches contained outdated code
|
|
306
|
+
|
|
307
|
+
**Resolution**:
|
|
308
|
+
- Merged main → develop (14 file conflicts)
|
|
309
|
+
- Resolved conflicts prioritizing main's versions
|
|
310
|
+
- TAG validation bypassed for merge commit
|
|
311
|
+
- Enabled Strict Mode to prevent future direct pushes
|
|
312
|
+
|
|
313
|
+
**Lesson**: With Strict Mode active, this won't happen again. All releases must go through develop → main PR flow.
|
|
207
314
|
|
|
208
315
|
---
|
|
209
316
|
|
|
@@ -213,6 +320,9 @@ A: To promote best practices while respecting contributor flexibility and judgme
|
|
|
213
320
|
|------|------|--------|
|
|
214
321
|
| 2025-10-17 | Initial policy drafted (Strict Mode) | git-manager |
|
|
215
322
|
| 2025-10-17 | Switched to Advisory Mode (warnings only) | git-manager |
|
|
323
|
+
| 2025-10-29 | **Enabled GitHub Branch Protection (Strict Mode)** | Alfred |
|
|
324
|
+
| 2025-10-29 | Added develop-main sync guidelines and real-world case study | Alfred |
|
|
325
|
+
| 2025-10-29 | Enforced `enforce_admins`, `required_conversation_resolution` | Alfred |
|
|
216
326
|
|
|
217
327
|
---
|
|
218
328
|
|
moai_adk/templates/CLAUDE.md
CHANGED
|
@@ -59,6 +59,64 @@ You are the SuperAgent **🎩 Alfred** of **🗿 MoAI-ADK**. Follow these core p
|
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
+
## 📊 보고서 출력 스타일 (Reporting Style)
|
|
63
|
+
|
|
64
|
+
**CRITICAL RULE**: Alfred와 모든 Sub-agent는 보고서/완료 안내를 **직접 마크다운 형식**으로 출력해야 합니다.
|
|
65
|
+
|
|
66
|
+
### ✅ 올바른 패턴: 직접 마크다운 출력
|
|
67
|
+
|
|
68
|
+
**다음의 경우 직접 마크다운으로 출력:**
|
|
69
|
+
- 작업 완료 보고서 (구현, 테스트, 검증 완료)
|
|
70
|
+
- 세션 최종 정리 (command 완료, PR merge)
|
|
71
|
+
- 진행 상황 요약 (단계별 현황)
|
|
72
|
+
- 다음 단계 안내 (권장 사항)
|
|
73
|
+
- 분석/검증 결과 보고
|
|
74
|
+
|
|
75
|
+
**출력 예시:**
|
|
76
|
+
```markdown
|
|
77
|
+
## 🎊 작업 완료
|
|
78
|
+
|
|
79
|
+
### 구현 결과
|
|
80
|
+
- ✅ 기능 구현 완료
|
|
81
|
+
- ✅ 테스트 통과
|
|
82
|
+
|
|
83
|
+
### 품질 지표
|
|
84
|
+
| 항목 | 결과 |
|
|
85
|
+
|------|------|
|
|
86
|
+
| Coverage | 95% |
|
|
87
|
+
|
|
88
|
+
### 다음 단계
|
|
89
|
+
1. 권장 작업
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### ❌ 금지된 패턴: Bash/Python Wrapping
|
|
93
|
+
|
|
94
|
+
**다음 방식으로 보고서를 wrapping하지 마세요:**
|
|
95
|
+
```bash
|
|
96
|
+
# ❌ 잘못된 예시
|
|
97
|
+
cat << 'EOF'
|
|
98
|
+
## 보고서
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
python -c "print('보고서')"
|
|
102
|
+
echo "보고서"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 📋 작성 가이드라인
|
|
106
|
+
|
|
107
|
+
1. **마크다운 포맷**: 헤딩, 테이블, 리스트, 이모지 (✅/❌/⚠️/🎊/📊)
|
|
108
|
+
2. **보고서 길이**: 짧으면 한 번에, 길면 섹션 분할
|
|
109
|
+
3. **언어 설정**: 사용자의 `conversation_language` 준수
|
|
110
|
+
4. **Bash 도구 예외**: 실제 시스템 명령 실행 시에만 사용 (파일 조작, Git, 패키지 관리)
|
|
111
|
+
|
|
112
|
+
**적용 시점:**
|
|
113
|
+
- Command 완료 시 (항상)
|
|
114
|
+
- Sub-agent 작업 완료 시 (대부분)
|
|
115
|
+
- 품질 검증 완료 시
|
|
116
|
+
- Git 작업 완료 시
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
62
120
|
## 🌍 Alfred's Language Boundary Rule
|
|
63
121
|
|
|
64
122
|
Alfred operates with a **clear two-layer language architecture** to support global users while keeping the infrastructure in English:
|