Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,28 @@ Layer 7: Visualization (Streamlit Dashboard - 시각화만)
- `config/constitution.yaml` - 헌법 전문 (800+ 줄)
- `NORTH_STAR.md` - 1분 읽기, 방향성 재확인
- `.github/TASK_TEMPLATE.md` - 작업 명세 템플릿
- `docs/CONSTITUTION_ONBOARDING_GUIDE.md` - 헌법 온보딩 워크숍 로드맵
- `docs/LAYER4_7_IMPLEMENTATION.md` - Layer 4~7 최적화·시각화 구현 예시

---

## 🚀 빠른 시작 (5분)

### Step 0: 헌법 온보딩 & 거버넌스 세팅

1. `docs/CONSTITUTION_ONBOARDING_GUIDE.md`와 `NORTH_STAR.md`를 읽고 팀 맞춤 체크리스트를 작성합니다.
2. 거버넌스 템플릿 복사 및 편집:
```bash
mkdir -p RUNS/drafts
cp templates/governance/p11-p13-checklist.yaml RUNS/drafts/DECISION-DEMO.yaml
# 편집 후 검증 및 Evidence 저장
python scripts/governance_gate.py \
--checklist RUNS/drafts/DECISION-DEMO.yaml \
--task-id DEMO-000 \
--output-dir RUNS/evidence
```
3. 거버넌스 Evidence (`RUNS/evidence/DEMO-000/governance.json`)가 생성되었는지 확인합니다.

### Option 1: Bash Wrapper (권장 - Linux/macOS/Git Bash)

`setup.sh`는 내부적으로 `setup.py`를 호출하며, 실패 시 자동 롤백 기능을 포함합니다.
Expand Down Expand Up @@ -221,6 +238,16 @@ npm run release -- --dry-run
# 참고: 루트에 `.nvmrc`(Node 20)를 제공하므로 `nvm use` 혹은 `corepack enable` 환경에서 맞춰 실행하세요.
```

### Step 4: ROI & 대시보드 초기화

```bash
# ROI 리포트 생성 및 최신 JSON 출력 저장
python scripts/roi_report.py --governance-reviews 1 --compliance-rate 98.5

# (선택) Streamlit 대시보드에서 활용
streamlit run streamlit_app.py
```

---

## 📁 디렉토리 구조
Expand Down Expand Up @@ -324,6 +351,8 @@ projects:
**브레이크이븐**: 3.2개월
**연간 절감**: 264시간 (33일)

> ROI 수치는 `scripts/roi_report.py`를 실행해 팀별 입력값으로 즉시 재계산할 수 있습니다.

---

## 🆘 문제 해결
Expand Down
87 changes: 87 additions & 0 deletions docs/CONSTITUTION_ONBOARDING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Constitution Onboarding Guide

> **목적**: `config/constitution.yaml`을 조직 상황에 맞게 빠르게 조정하고, 실행형 자산 시스템을 온보딩할 때 흔히 겪는 시행착오를 줄이기 위한 가이드입니다.

---

## 1. 준비 체크리스트 (30분)

| 단계 | 설명 | 참고 자료 |
|------|------|-----------|
| 1 | NORTH_STAR.md를 읽어 핵심 철학 복습 | `NORTH_STAR.md` |
| 2 | 현재 조직의 필수 규범 정리 | 내부 표준 문서 |
| 3 | 적용 범위 확정 (팀/프로젝트/PoC) | OKR, 프로젝트 계획 |
| 4 | TaskExecutor 데모 실행 | `scripts/demo_executor.py` |

> **TIP**: 첫 워크숍에서는 `config/constitution.yaml` 전체를 다루지 말고, 반드시 필요한 조항(P1~P10)만 골라 적용 범위를 정하세요.

---

## 2. 조항 맞춤화 절차 (Half-day Workshop)

1. **핵심 조항 선별**
- 조직 필수 규범을 P1~P10과 매핑합니다.
- 겹치는 항목은 기존 조항에 설명을 추가하고, 없는 항목만 별도 조항으로 작성합니다.
2. **거버넌스 조항(P11~P13) 시뮬레이션**
- 최근 결정 사례 1~2개를 선택해 P11~P13 체크리스트를 적용해봅니다.
- 누락된 승인 단계나 기록 포인트가 있는지 확인합니다.
3. **체크리스트 작성**
- `templates/governance/p11-p13-checklist.yaml`을 복제해 팀 상황에 맞게 수정합니다.
- TaskExecutor 실행 전에 체크리스트를 채우도록 표준 운영 절차(SOP)에 포함합니다.
4. **증거 경로 설정**
- `RUNS/evidence/` 하위에 팀별 폴더 구조를 합의하고, ObsidianVault 경로를 설정합니다.

> **워크숍 산출물**: 수정된 `constitution.yaml`, 맞춤형 거버넌스 체크리스트, 증거 수집 경로 정의 문서.

---

## 3. 역할별 온보딩 가이드

### 헌법 관리자 (Lead)
- Constitution 변경 Pull Request 리뷰 책임.
- `scripts/governance_gate.py`를 실행해 체크리스트가 최신 상태인지 확인합니다.
- 분기별로 ROI 리포트를 생성해 경영진에 공유합니다.

### TaskExecutor 운영자
- 작업 시작 전 `templates/governance/p11-p13-checklist.yaml` 작성 상태를 확인합니다.
- `scripts/enhanced_task_executor.py` 실행 시 `--force` 플래그 사용을 최소화하고, 경고 로그를 Evidence에 저장합니다.

### 의사결정 승인자
- P11~P13 문항 중 *승인* 항목에 대한 최종 결정을 내립니다.
- 승인 기록은 `RUNS/evidence/<task_id>/governance.json`에 자동 저장되는지 점검합니다.

---

## 4. 30일 운영 로드맵

| 주차 | 목표 | 실행 포인트 |
|------|------|-------------|
| 1주차 | 파일 구조 정리 및 TaskExecutor 데모 | 샘플 YAML 계약서 실행, Evidence 자동 수집 확인 |
| 2주차 | Constitution 맞춤화 초안 완성 | 워크숍 진행, 거버넌스 체크리스트 초안 작성 |
| 3주차 | 거버넌스 자동화 파일 연결 | `scripts/governance_gate.py` 통합, Evidence 저장 경로 검증 |
| 4주차 | ROI 대시보드 초안 발표 | `scripts/roi_report.py`로 KPI 생성, Streamlit 보조 화면 구성 |

---

## 5. Troubleshooting FAQ

**Q1. 조항이 너무 많아서 팀이 부담스러워합니다.**
A. `config/constitution.yaml`에서 필수 조항만 남긴 *Lite 버전*을 만들어 첫 달에 사용하세요. 나머지는 체크리스트를 통해 단계적으로 도입합니다.

**Q2. 증거 저장이 누락됩니다.**
A. `scripts/governance_gate.py` 실행 시 Evidence 경로 유효성을 검증합니다. 경로가 없으면 자동 생성하고, README에 기록하십시오.

**Q3. ROI가 체감되지 않습니다.**
A. `scripts/roi_report.py`의 입력값 중 "절약 시간"을 실제 팀 데이터를 기반으로 업데이트하세요. Streamlit 대시보드에는 최대 3개의 KPI만 노출하여 집중도를 높입니다.

---

## 6. 다음 단계

1. `docs/LAYER4_7_IMPLEMENTATION.md`를 읽고 최적화~시각화 계층의 구현 예시를 검토합니다.
2. `scripts/roi_report.py`를 실행하여 첫 번째 ROI 리포트를 생성하고, 경영진 브리핑 자료에 포함합니다.
3. 헌법 변경이 필요한 경우 `templates/governance/p11-p13-checklist.yaml`을 통해 P13 절차를 수행한 뒤 Pull Request를 올립니다.

---

**최종 목표**: 모든 의사결정과 실행 자료가 Constitution 조항과 Evidence로 연결되어, 새로운 팀원이 합류해도 즉시 동일한 실행형 자산 시스템을 재현할 수 있도록 하는 것입니다.
82 changes: 82 additions & 0 deletions docs/LAYER4_7_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Layer 4~7 Implementation Playbook

> **목적**: Optimization → Evidence → Knowledge Asset → Visualization 계층을 빠르게 체험할 수 있는 최소 구현 사례를 제공합니다.

---

## 1. Layer 4 - Optimization

### 1.1 VerificationCache 활성화
- 실행: `python scripts/verification_cache.py --build` (기본 캐시 생성)
- YAML 계약서 내 `use_verification_cache: true` 옵션을 추가하여 재실행 시 중복 검증을 건너뜁니다.
- Evidence: `RUNS/<task_id>/cache/`에 해시 정보가 저장됩니다.

### 1.2 CriticalFileDetector 적용
- 실행: `python scripts/critical_file_detector.py --path src/ --top 5`
- 출력: 변경 위험도가 높은 파일 목록(JSON + Markdown)
- 활용: P4, P5 조항 위반 가능성이 높은 파일에 우선적으로 리뷰 리소스를 배정합니다.

---

## 2. Layer 5 - Evidence Collection

| 작업 | 자동화 포인트 | Evidence 위치 |
|------|---------------|----------------|
| TaskExecutor 실행 | `AutomaticEvidenceTracker`가 실행 로그 및 해시 기록 | `RUNS/evidence/<task_id>/execution.json` |
| 거버넌스 체크리스트 | `scripts/governance_gate.py`가 체크리스트와 승인자를 수집 | `RUNS/evidence/<task_id>/governance.json` |
| 모델 출력 기록 | `AutomaticEvidenceTracker`의 `model_outputs` 옵션 | `RUNS/evidence/<task_id>/models/` |

> **팁**: Evidence 폴더 구조를 README에 정의하면 신규 팀원 온보딩 속도가 빨라집니다.

---

## 3. Layer 6 - Knowledge Asset

1. `scripts/obsidian_bridge.py --vault ~/Obsidian/ExecutableAssets`
2. 위 명령을 TaskExecutor 후크로 등록하면 Evidence가 Obsidian Vault에 동기화됩니다.
3. Vault 구조 예시:
```
Vault/
01_Constitution/
02_Tasks/
03_Evidence/
2024-ROI-Report.md
P11-Conflict-Review.md
```
4. Obsidian에서 Dataview 플러그인을 사용하면 Evidence 메타데이터를 표 형태로 조회할 수 있습니다.

---

## 4. Layer 7 - Visualization (Streamlit)

### 4.1 필수 KPI 스크립트 연동
- `scripts/roi_report.py`의 출력(JSON)을 Streamlit에서 읽어 차트로 표시합니다.
- 최소 KPI: `time_saved_hours`, `constitution_compliance_rate`, `governance_reviews_completed`.

### 4.2 대시보드 최소 레이아웃 예시
```python
import json
import streamlit as st

with open("RUNS/reports/latest_roi.json", "r", encoding="utf-8") as f:
data = json.load(f)

st.title("Constitution Execution Dashboard")
st.metric("연간 절약 시간", f"{data['time_saved_hours']}시간")
st.metric("헌법 준수율", f"{data['constitution_compliance_rate']}%")
st.metric("거버넌스 리뷰 수", data['governance_reviews_completed'])
```

---

## 5. 운영 체크리스트

| 주기 | 항목 | 담당 |
|------|------|------|
| 매 실행 | `scripts/governance_gate.py` 실행 후 TaskExecutor 시작 | 운영자 |
| 주간 | VerificationCache 재생성 및 CriticalFileDetector 리포트 검토 | 테크 리드 |
| 월간 | `scripts/roi_report.py` 실행, Streamlit 스냅샷 업데이트 | 헌법 관리자 |

---

**다음 단계**: Streamlit 대시보드를 운영하기 전에 ROI 리포트와 Evidence 폴더 구조가 안정화되었는지 확인하고, 필요 시 `docs/CONSTITUTION_ONBOARDING_GUIDE.md`에 있는 워크숍을 반복하세요.
167 changes: 167 additions & 0 deletions scripts/governance_gate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""Governance Gate

P11~P13 거버넌스 체크리스트를 검증하고 Evidence 저장 경로에 기록합니다.

Usage:
python scripts/governance_gate.py --checklist path/to/checklist.yaml --task-id FEAT-123 --output-dir RUNS/evidence
"""

from __future__ import annotations

import argparse
import json
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Tuple

import yaml


PLACEHOLDER_VALUES = {"", "TBD", "TODO"}


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Validate governance checklist and persist evidence")
parser.add_argument("--checklist", type=Path, required=True, help="Path to the completed P11~P13 checklist YAML")
parser.add_argument("--task-id", type=str, default=None, help="Task identifier used for evidence storage")
parser.add_argument(
"--output-dir",
type=Path,
default=Path("RUNS/evidence"),
help="Base directory for evidence output (default: RUNS/evidence)",
)
parser.add_argument(
"--fail-on-warning",
action="store_true",
help="Treat warnings as errors to block execution when required fields are missing",
)
return parser.parse_args()


def load_checklist(path: Path) -> Dict[str, Any]:
if not path.exists():
raise FileNotFoundError(f"Checklist file not found: {path}")
with path.open("r", encoding="utf-8") as f:
data = yaml.safe_load(f)
if not isinstance(data, dict):
raise ValueError("Checklist must be a YAML mapping")
return data


def is_placeholder(value: Any) -> bool:
if value is None:
return True
if isinstance(value, str):
return value.strip() in PLACEHOLDER_VALUES
if isinstance(value, list):
return all(is_placeholder(item) for item in value)
if isinstance(value, dict):
return all(is_placeholder(v) for v in value.values())
return False


def ensure_fields(data: Dict[str, Any]) -> Tuple[List[str], List[str]]:
"""Return (errors, warnings) detected in the checklist."""

errors: List[str] = []
warnings: List[str] = []

meta = data.get("meta", {})
for key in ["decision_title", "prepared_by", "date"]:
if is_placeholder(meta.get(key)):
errors.append(f"meta.{key} is missing")

reviewers = meta.get("reviewers")
if not reviewers or is_placeholder(reviewers):
warnings.append("meta.reviewers should list at least one reviewer")

p11 = data.get("p11_conflict_review", {})
if is_placeholder(p11.get("conflict_summary")):
errors.append("p11_conflict_review.conflict_summary is missing")
if is_placeholder(p11.get("resolution")):
errors.append("p11_conflict_review.resolution is missing")
if is_placeholder(p11.get("related_principles")):
warnings.append("p11_conflict_review.related_principles should reference at least one article")
if is_placeholder(p11.get("past_decisions_consulted")):
warnings.append("p11_conflict_review.past_decisions_consulted should cite prior work")

p12 = data.get("p12_tradeoff_analysis", {})
options = p12.get("options") or []
cleaned_options = [opt for opt in options if not is_placeholder(opt.get("name"))]
if not cleaned_options:
errors.append("p12_tradeoff_analysis.options must describe at least one option")
for index, option in enumerate(cleaned_options, start=1):
if is_placeholder(option.get("pros")):
warnings.append(f"p12_tradeoff_analysis.options[{index}].pros is empty")
if is_placeholder(option.get("cons")):
warnings.append(f"p12_tradeoff_analysis.options[{index}].cons is empty")
if is_placeholder(option.get("evidence")):
warnings.append(f"p12_tradeoff_analysis.options[{index}].evidence is empty")
if is_placeholder(p12.get("recommended_option")):
errors.append("p12_tradeoff_analysis.recommended_option is missing")
if is_placeholder(p12.get("rationale")):
errors.append("p12_tradeoff_analysis.rationale is missing")

p13 = data.get("p13_constitution_change", {})
if p13.get("required"):
if is_placeholder(p13.get("target_articles")):
errors.append("p13_constitution_change.target_articles is missing")
if is_placeholder(p13.get("change_summary")):
errors.append("p13_constitution_change.change_summary is missing")
if is_placeholder(p13.get("approval_status")):
errors.append("p13_constitution_change.approval_status is missing")
else:
if not p13:
warnings.append("p13_constitution_change section is missing; set required=false when no change is needed")

signoff = data.get("signoff", {})
if is_placeholder(signoff.get("approver")):
errors.append("signoff.approver is missing")
if is_placeholder(signoff.get("approval_date")):
warnings.append("signoff.approval_date is missing")

return errors, warnings


def persist_evidence(data: Dict[str, Any], output_dir: Path, task_id: str | None) -> Path:
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
if task_id:
evidence_dir = output_dir / task_id
evidence_dir.mkdir(parents=True, exist_ok=True)
output_path = evidence_dir / "governance.json"
else:
reports_dir = output_dir / "reports"
reports_dir.mkdir(parents=True, exist_ok=True)
output_path = reports_dir / f"governance_{timestamp}.json"

payload = {
"generated_at": timestamp,
"task_id": task_id,
"checklist": data,
}
with output_path.open("w", encoding="utf-8") as f:
json.dump(payload, f, indent=2, ensure_ascii=False)
Comment on lines +137 to +143

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge JSON evidence serialization fails when checklist contains YAML dates

The governance gate persists the parsed checklist directly into JSON (json.dump(payload, ...)). Because yaml.safe_load converts unquoted ISO timestamps like 2024-12-10 into datetime.date objects, a checklist that uses natural YAML date syntax will hit TypeError: Object of type date is not JSON serializable when writing evidence, even though validation succeeds. This prevents any evidence from being stored unless every date field is manually quoted. Consider normalizing non‑string values (e.g., via default=str or converting dates to ISO strings) before dumping so the tool accepts typical YAML input.

Useful? React with 👍 / 👎.

return output_path


def main() -> None:
args = parse_args()
checklist = load_checklist(args.checklist)
errors, warnings = ensure_fields(checklist)

if errors:
for msg in errors:
print(f"[ERROR] {msg}")
if warnings:
for msg in warnings:
print(f"[WARN] {msg}")

if errors or (warnings and args.fail_on_warning):
raise SystemExit("Governance gate validation failed")

output_path = persist_evidence(checklist, args.output_dir, args.task_id)
print(f"[OK] Governance evidence stored at {output_path}")


if __name__ == "__main__":
main()
Loading
Loading