← Phase 2 Phase 2 • Week 2 Delivery

Local SE Agent Masterclass

MacBook M3 16GB • PR Synthesis & Impact Engine
📍 ส่วนที่ 1: หมุดตั้งโจทย์ & วัตถุประสงค์หลัก (Title Connection)

Chapter 25 — Pull Request Synthesis & Impact Assessment

ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?

ใน Day 23 และ 24 ระบบ Autonomous Agent ของเราสามารถทดลองแก้บั๊กใน Git Worktree และผ่าน Verification Loop ได้อย่างปลอดภัย ทว่าในบริบท Enterprise การ Push โค้ดดิบๆ ขึ้นไปเปิด PR โดยปราศจากบริบท จะสร้างภาระมหาศาลให้กับทีม Senior Engineer ในการ Review

ใน Day 25 เราจึงยกระดับ Agent ด้วยระบบ PR Synthesis & Impact Assessment: นำเอา intelligence/call_graph.py (จาก Day 19) และ intelligence/ast_parser.py (จาก Day 18) มาวิเคราะห์ Unified Diff เพื่อคำนวณ Blast Radius แล้วสังเคราะห์รายงานอธิบาย Pull Request แบบมืออาชีพ (Executive Summary, Technical Changes, Blast Radius Analysis, Test Verification) เป็น Markdown ที่พร้อมยิงเข้า GitHub ทันที

✓ Day 24: Enterprise Delivery & PR Day 25: PR Synthesis & Impact Assessment 📝✨ Day 26: Architecture Review Skill
📂 ส่วนที่ 2: ผังโครงสร้างโปรเจกต์จริง & ตำแหน่งไฟล์ (Project File Structure)

ใน Day 25 เราจัดทำ Skill พิเศษสำหรับสังเคราะห์รายงาน Impact Analysis และรายละเอียด PR เป็นรูปแบบ Markdown ที่อ่านง่าย:

se_agent/
├── requirements.txt                       # Dependencies
├── config.json                             # Model config & limits
├── se-agent                                # CLI wrapper
├── main.py                                 # Interactive CLI Cockpit
├── test_pr_synthesis.py                  # [ไฟล์ใหม่ Day 25] สคริปต์จำลองการรันระบบ PR Synthesis และสร้างรายงาน Impact Assessment
├── skills/                                 # Pluggable Skill Cartridges
│   ├── registry.py
│   ├── lifecycle.py
│   ├── unit_test_synthesizer/
│   └── pr_synthesizer/                    # [สกิลใหม่ Day 25] สกิลสำหรับการสังเคราะห์ PR และประเมินผลกระทบ
│       ├── metadata.json                    # Stage 1 Discovery Card ของ PR Synthesizer Skill
│       ├── instructions.md                  # Stage 2 Deep Operational Rules สำหรับวิเคราะห์ Git Diff และ Blast Radius
│       └── tools.py                         # Dedicated Tool: generate_impact_report และ create_pull_request
├── workspace/                              # Sandbox & Verification
├── delivery/                               # PR Automation
├── intelligence/                           # Code Intelligence & Parsing
├── memory/                                # Sliding Window Context & ChromaDB
└── core/                                  # ReAct Engine, Dispatcher, Guardrails, Workspace
📍 skills/pr_synthesizer/tools.py ทำหน้าที่อะไร?

จัดเตรียมฟังก์ชันเฉพาะทางสำหรับวิเคราะห์ Git Diff ร่วมกับข้อมูล Call Graph เพื่อประเมินผลกระทบ (Blast Radius) และสังเคราะห์ออกเป็นรายงาน Markdown สำหรับใช้ประกอบการเปิด Pull Request

📍 test_pr_synthesis.py ทำหน้าที่อะไร?

สคริปต์ทดสอบการทำงานของตลับสกิล pr_synthesizer ในการอ่านผลลัพธ์การเปลี่ยนแปลงโค้ดและแปลงเป็นรายงาน PR Description ที่สมบูรณ์แบบโดยอัตโนมัติ

🏛 ส่วนที่ 3: สถาปัตยกรรม PR Synthesis & Blast Radius (Mental Model)
แผนผังกระบวนการสังเคราะห์ Pull Request และประเมินผลกระทบ
1. Semantic Git Diff Sandbox Diff

สกัด Git Diff เปรียบเทียบระหว่าง Sandbox Branch และ origin/main พร้อมกรองไฟล์ Config หรือ Test ที่ไม่เกี่ยวข้อง

2. Blast Radius Mapping Graph Traversal

ยิงชื่อ Signature เมธอดที่ถูกแก้ไขเข้าสู่ Call Hierarchy Directed Graph เพื่อหา Downstream Consumer ทุกจุดในโปรเจกต์

3. PR Template Assembly Markdown Synthesis

สังเคราะห์ Impact Matrix, ผลลัพธ์ Test Coverage Delta, และประกอบเป็น GitHub CLI Command (gh pr create)

💻 ส่วนที่ 4: ซอร์สโค้ดจริง & คำอธิบายทีละบรรทัด (Line-by-Line Implementation)

คลิกเลือกดูซอร์สโค้ดของแต่ละไฟล์ที่ต้องสร้างขึ้นในวันนี้:

{
  "name": "pr_synthesizer",
  "version": "1.0.0",
  "description": "Synthesizes Pull Request descriptions, evaluates cross-layer Blast Radius, and automates PR submission via GitHub CLI.",
  "triggers": [
    "create pr",
    "open pull request",
    "impact analysis",
    "summarize changes",
    "review diff"
  ],
  "required_tools": [
    "extract_semantic_diff",
    "calculate_blast_radius",
    "submit_github_pr"
  ],
  "author": "Autonomous SE Agent"
}
# PR Synthesizer - Operational Playbook

## 1. Context & Role
You are an Enterprise Release Engineer and Technical Lead. Your job is to transform raw code diffs and verification outputs into concise, risk-aware Pull Request documentation.

## 2. Impact Risk Taxonomy (Blast Radius Matrix)
When evaluating changed methods via the Call Graph, categorize risk:
- **LOW RISK**: Changes contained entirely within private helper methods or isolated unit tests.
- **MEDIUM RISK**: Changes to public methods called by 1-3 downstream controllers or internal services.
- **HIGH RISK**: Changes to Core Domain Interfaces, Entities, or methods called by >3 distinct modules or payment pipelines.

## 3. Mandatory Markdown Structure
Every generated PR Description must follow this schema:
1. **Executive Summary**: 2-3 sentences explaining the "Why" and "What".
2. **Impact Assessment**: Risk Level + Downstream Affected Components list.
3. **Verification Summary**: Test execution metrics (Total / Passed / Duration).
4. **Architectural Diff Notes**: Brevity bullet points of key structural modifications.
5. **Reviewer Checklist**: Clear verification criteria for peer engineers.
import subprocess
from typing import Dict, List, Any
from pathlib import Path

def extract_semantic_diff(worktree_dir: str, base_branch: str = "main") -> Dict[str, Any]:
    """Extracts git diff and list of changed files from the isolated worktree."""
    diff_cmd = ["git", "-C", worktree_dir, "diff", f"{base_branch}...HEAD"]
    name_cmd = ["git", "-C", worktree_dir, "diff", "--name-only", f"{base_branch}...HEAD"]

    diff_res = subprocess.run(diff_cmd, capture_output=True, text=True)
    name_res = subprocess.run(name_cmd, capture_output=True, text=True)

    files = [f.strip() for f in name_res.stdout.splitlines() if f.strip()]
    return {
        "raw_diff": diff_res.stdout,
        "modified_files": files,
        "files_count": len(files)
    }

def calculate_blast_radius(modified_files: List[str], call_graph) -> Dict[str, Any]:
    """Computes upstream callers and calculates the risk rating."""
    affected_nodes = []
    for f in modified_files:
        symbol_name = Path(f).stem
        if hasattr(call_graph, "get_downstream_dependents"):
            deps = call_graph.get_downstream_dependents(symbol_name)
            affected_nodes.extend(deps)

    unique_affected = sorted(list(set(affected_nodes)))
    risk_level = "LOW"
    if len(unique_affected) > 4:
        risk_level = "HIGH"
    elif len(unique_affected) >= 1:
        risk_level = "MEDIUM"

    return {
        "risk_level": risk_level,
        "affected_consumers": unique_affected
    }

def format_pr_markdown(title: str, summary: str, blast_data: Dict[str, Any], test_results: str) -> str:
    """Assembles the final GitHub-flavored markdown description."""
    consumers_list = "\n".join([f"- `{c}`" for c in blast_data.get("affected_consumers", [])]) or "- None (Isolated changes)"
    return f"""# {title}

### Executive Summary
{summary}

### Impact Assessment & Blast Radius
- **Assessed Risk**: `{blast_data['risk_level']}`
- **Downstream Consumers Impacted**:
{consumers_list}

### Automated Verification Suite
{test_results}

### Peer Review Checklist
* [ ] Business logic matches domain constraints
* [ ] No regression on downstream consumers listed above
* [ ] Verified locally on Apple Silicon / macOS M3
"""
from skills.pr_synthesizer.tools import calculate_blast_radius, format_pr_markdown


class MockCallGraph:
    """Mocking the Day 19 Call Hierarchy Graph."""
    def get_downstream_dependents(self, class_name: str):
        mapping = {
            "PaymentService": ["CheckoutController", "SubscriptionScheduler", "OrderAuditConsumer"],
            "StringUtils": []
        }
        return mapping.get(class_name, [])


def test_pr_synthesis_flow():
    print("=== Testing Day 25: PR Synthesis & Impact Assessment ===")

    # 1. Simulate modified files and call graph lookup
    modified = ["src/main/java/com/service/PaymentService.java"]
    graph = MockCallGraph()
    blast = calculate_blast_radius(modified, graph)

    print(f"[+] Assessed Risk: {blast['risk_level']}")
    print(f"[+] Impacted Consumers: {blast['affected_consumers']}")

    # 2. Format the PR description
    summary = "Resolved NullPointerException when token expires during async gateway dispatch."
    test_run = "Results: 14 passed, 0 failed, 0 errors (Elapsed: 3.42s)"

    pr_doc = format_pr_markdown(
        title="fix(payment): prevent NPE on expired gateway token",
        summary=summary,
        blast_data=blast,
        test_results=test_run
    )

    print("\n--- Synthesized PR Body Preview ---")
    print(pr_doc)
    assert blast["risk_level"] == "MEDIUM"
    print("\nDay 25 Skill Verification Passed Successfully!")


if __name__ == "__main__":
    test_pr_synthesis_flow()
⚡ ส่วนที่ 5: ปฏิบัติการจริงบน Terminal Mac M3 (Step-by-Step Lab)

เปิด Terminal บน MacBook M3 แล้วทำตามขั้นตอนเพื่อติดตั้งและทดสอบ PR Synthesizer:

ขั้นตอนที่ 1: สร้างโฟลเดอร์สำหรับ Skill ใหม่
mkdir -p skills/pr_synthesizer
ขั้นตอนที่ 2: วางโค้ดลงในแต่ละไฟล์

- นำโค้ดใน ส่วนที่ 4 (metadata.json) ไปวางใน skills/pr_synthesizer/metadata.json
- นำโค้ดใน ส่วนที่ 4 (instructions.md) ไปวางใน skills/pr_synthesizer/instructions.md
- นำโค้ดใน ส่วนที่ 4 (tools.py) ไปวางใน skills/pr_synthesizer/tools.py
- นำโค้ดใน ส่วนที่ 4 (test_pr_synthesis.py) ไปวางในรูทของโปรเจกต์

ขั้นตอนที่ 3: สั่งรันสคริปต์ทดสอบ
python test_pr_synthesis.py

ผลลัพธ์: ระบบประเมินระดับความเสี่ยง (Blast Radius) เป็น MEDIUM และสร้างโครงสร้าง Markdown สำหรับเปิด Pull Request

📝 ส่วนที่ 6: ชุดควิซประเมินความรู้ระดับสถาปนิก (ตอบปุ๊บรู้ผลทันที)

ข้อที่ 1: ภายใต้สถาปัตยกรรม Two-Stage Progressive Disclosure ไฟล์ instructions.md จะถูกโหลดเมื่อใด?

🏁 สรุปหมุดเชื่อมโยง (Chapter Wrap-up)

เนื้อหาใน Day 25 — Pull Request Synthesis & Impact Assessment เติมตลับสกิล pr_synthesizer เข้าไปในวงจรส่งมอบ: อ่าน Git Diff ผสาน Call Graph แล้วสังเคราะห์รายงาน Markdown สำหรับเปิด Pull Request โดยไม่ต้องให้ Reviewer นั่งไล่ Diff เองทั้งหมด