From c5968b198d06135b4b41d0ddda026ad1b09c8a5c Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:13:25 +0000 Subject: [PATCH 1/7] forge: open work branch for iss-6da9ba883259-attempt-1-run-99a8b746f8fa --- .forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa.md diff --git a/.forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa.md b/.forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa.md new file mode 100644 index 0000000..145a1ce --- /dev/null +++ b/.forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa.md @@ -0,0 +1,3 @@ +# iss-6da9ba883259-attempt-1-run-99a8b746f8fa + +Forge 이슈 작업 브랜치 `forge/iss-6da9ba883259-attempt-1-run-99a8b746f8fa`. From 770c3e830fa0ae3a72a87ea83debd31088ccd608 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:15 +0000 Subject: [PATCH 2/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/SPEC.md | 171 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 planning-contract/SPEC.md diff --git a/planning-contract/SPEC.md b/planning-contract/SPEC.md new file mode 100644 index 0000000..7b9fb36 --- /dev/null +++ b/planning-contract/SPEC.md @@ -0,0 +1,171 @@ +# 계획 계약 명세서 (Planning Contract Specification) + +## 개요 + +MiniMax 기반 에이전트의 계획 계약은 에이전트가 수립한 계획의 품질, 완전성, 실행 가능성을 보장하기 위한 명세서이다. 본 문서는 계약의 각 조항과 그에 대한 검증 기준을 정의한다. + +--- + +## 용어 정의 + +| 용어 | 정의 | +|------|------| +| **계약 (Contract)** | 에이전트의 계획 수립 행위에 대한 명시적 약속 및 조건 | +| **조항 (Clause)** | 계약의 구성 단위로서 특정 요구사항을 표현 | +| **검증 기준 (Verification Criteria)** | 조항의 성립 여부를 판단하는 성공/실패 조건 | +| **계획 (Plan)** | 목표 달성을 위한 단계별 행동 시퀀스 | +| **에피타이드 (Epitaph)** | 계획 실행 결과의 기록 및 평가 | + +--- + +## 계약 조항 명세 + +### 조항 1: 목표 명확성 (Goal Clarity) + +**설명**: 수립된 계획은 명확하고 측정 가능한 목표를 포함해야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 목표가 구체적인 수치 또는 상태로 표현됨
- 목표 달성을 판단할 수 있는 명확한 기준 존재
- 목표가 단일 책임 원칙을 따름 (하나의 주요 결과) | +| **실패 조건** | - 목표가 모호한 표현으로 기술됨 (예: "좋게 만들기")
- 성공/실패 판단 기준이 없음
- 하나의 계획에 두 개 이상의 독립적 목표 포함 | + +--- + +### 조항 2: 단계 완전성 (Step Completeness) + +**설명**: 계획은 목표 달성까지 필요한 모든 단계를 포함해야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 각 단계가 선행 조건을 만족함
- 모든 의존성 단계가 순서대로 배치됨
- 최종 단계에서 목표 달성 보장 | +| **실패 조건** | - 필수 단계가 누락됨
- 단계 간 의존성 순서 위반
- 목표에 도달하지 않는 불완전한 시퀀스 | + +--- + +### 조항 3: 실행 가능성 (Executability) + +**설명**: 계획의 각 단계는 현재 환경에서 실행 가능한 명령이어야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 각 단계의 명령어가 시스템에서 사용 가능함
- 필요한 리소스(파일, 권한, 도구)가 확보됨
- 단계 실행에 대한 타임아웃이 설정됨 | +| **실패 조건** | - 존재하지 않는 명령어 또는 도구 참조
- 필요한 리소스 접근 권한 없음
- 무한 대기 가능성이 있는 단계 포함 | + +--- + +### 조항 4: 리스크 평가 (Risk Assessment) + +**설명**: 계획은 잠재적 리스크를 식별하고 완화 전략을 포함해야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 각 단계별 잠재적 리스크 명시됨
- 리스크 발생 시 대안 경로 또는 복구 전략 존재
- 중요 리스크에 대한 회피 또는 감소 조치 포함 | +| **실패 조건** | - 리스크 식별 없이 진행
- 복구 전략 없는 단일 실패 지점 존재
- 치명적 리스크에 대한 무시 | + +--- + +### 조항 5: 검증 가능성 (Verifiability) + +**설명**: 계획의 각 단계는 실행 후 결과를 검증할 수 있어야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 각 단계에 대한 예상 결과 명시됨
- 성공/실패를 판단하는 어설션 존재
- 검증 방법이 자동화 가능함 | +| **실패 조건** | - 단계 실행 결과 확인 방법 없음
- 주관적 판단에만 의존하는 검증
- 검증 불가능한 상태 변경 포함 | + +--- + +### 조항 6: 시간적 제약 (Temporal Constraints) + +**설명**: 계획은 실행 시간에 대한 합리적인 제약 조건을 포함해야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 전체 계획에 대한 예상 소요 시간 명시
- 각 단계별 타임아웃 설정
- 시간 초과 시 대체 전략 존재 | +| **실패 조건** | - 시간 제약 없이 무한 대기 가능
- 현실적이지 않은 시간 추정
- 타임아웃 없는 장시간 작업 | + +--- + +### 조항 7: 롤백 가능성 (Rollback Capability) + +**설명**: 계획은 실패 시 이전 상태로 복구할 수 있는 메커니즘을 포함해야 한다. + +**검증 기준**: + +| 구분 | 조건 | +|------|------| +| **성공 조건** | - 각 변경 단계에 대한 롤백 명령 정의됨
- 롤백 실행 순서가 명확함
- 롤백 성공 여부 검증 방법 존재 | +| **실패 조건** | - 롤백 메커니즘 없음
- 일관성 없는 롤백 시퀀스
- 롤백 불가능한 영구 변경 포함 | + +--- + +## 계약 등급 + +| 등급 | 충족 조항 수 | 설명 | +|------|-------------|------| +| **A (최상)** | 7/7 | 모든 조항 충족, 최고 품질 보장 | +| **B (양호)** | 5-6/7 | 주요 조항 충족, 일부 개선 필요 | +| **C (미흡)** | 3-4/7 | 기본 구조는 있으나 신뢰성 낮음 | +| **D (위험)** | 1-2/7 | 심각한 결함, 실행 권장 불가 | +| **F (실패)** | 0/7 | 계약 미충족, 계획 부적격 | + +--- + +## 검증 프로세스 + +``` +┌─────────────┐ ┌──────────────┐ ┌─────────────┐ +│ 계획 수립 │───▶│ 계약 검증 │───▶│ 등급 부여 │ +└─────────────┘ └──────────────┘ └─────────────┘ + │ + ▼ + ┌──────────────┐ + │ 실패 조항 분석 │ + └──────────────┘ +``` + +--- + +## 에피타이드 기록 스키마 + +```json +{ + "planId": "string", + "timestamp": "ISO8601", + "grade": "A|B|C|D|F", + "clauses": { + "goalClarity": { "passed": boolean, "details": "string" }, + "stepCompleteness": { "passed": boolean, "details": "string" }, + "executability": { "passed": boolean, "details": "string" }, + "riskAssessment": { "passed": boolean, "details": "string" }, + "verifiability": { "passed": boolean, "details": "string" }, + "temporalConstraints": { "passed": boolean, "details": "string" }, + "rollbackCapability": { "passed": boolean, "details": "string" } + }, + "executionResult": { + "status": "success|partial|failure", + "duration": "number (ms)", + "deviationFromPlan": "string|null" + } +} +``` + +--- + +## 변경 이력 + +| 버전 | 날짜 | 변경 내용 | +|------|------|----------| +| 1.0.0 | 2026-07-14 | 초기 버전 작성 | From 0722d35164544bba2e791fc4c3cd7a23b4b62059 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:16 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/contract-validator.ts | 300 ++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 planning-contract/contract-validator.ts diff --git a/planning-contract/contract-validator.ts b/planning-contract/contract-validator.ts new file mode 100644 index 0000000..837b14f --- /dev/null +++ b/planning-contract/contract-validator.ts @@ -0,0 +1,300 @@ +/** + * 계획 계약 검증기 (Planning Contract Validator) + * + * 조항별 검증 로직을 구현하여 계획의 계약 충족 여부를 판단한다. + */ + +import { Plan, ClauseResult, ContractGrade, Epitaph } from './types'; + +/** + * 조항 1: 목표 명확성 검증 + */ +export function validateGoalClarity(plan: Plan): ClauseResult { + const issues: string[] = []; + + // 목표가 존재하는지 확인 + if (!plan.goal) { + issues.push('목표가 정의되지 않음'); + return { passed: false, issues }; + } + + // 목표가 구체적인지 확인 (모호한 표현 체크) + const vaguePatterns = [ + /좋게|잘|빠르게|최적으로|최대한/g, + /better|good|faster|optimal|best/g + ]; + + for (const pattern of vaguePatterns) { + if (pattern.test(plan.goal.description)) { + issues.push(`모호한 표현 감지: "${plan.goal.description.match(pattern)?.[0]}"`); + } + } + + // 측정 가능한 기준 존재 확인 + if (!plan.goal.criteria || plan.goal.criteria.length === 0) { + issues.push('목표 달성 판단 기준이 없음'); + } + + // 단일 책임 원칙 확인 + if (plan.goal.subGoals && plan.goal.subGoals.length > 2) { + issues.push('두 개 이상의 독립적 목표 포함 (단일 책임 위반)'); + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 2: 단계 완전성 검증 + */ +export function validateStepCompleteness(plan: Plan): ClauseResult { + const issues: string[] = []; + + if (!plan.steps || plan.steps.length === 0) { + issues.push('단계가 정의되지 않음'); + return { passed: false, issues }; + } + + // 각 단계의 선행 조건 확인 + for (let i = 0; i < plan.steps.length; i++) { + const step = plan.steps[i]; + + if (step.dependencies) { + for (const depId of step.dependencies) { + const depIndex = plan.steps.findIndex(s => s.id === depId); + if (depIndex === -1) { + issues.push(`단계 ${step.id}: 존재하지 않는 의존성 ${depId}`); + } else if (depIndex >= i) { + issues.push(`단계 ${step.id}: 의존성 ${depId}가 순서 위반 (${depIndex} >= ${i})`); + } + } + } + } + + // 최종 단계에서 목표 달성 확인 + const lastStep = plan.steps[plan.steps.length - 1]; + if (lastStep && plan.goal) { + if (!lastStep.produces?.some(p => plan.goal?.produces?.includes(p))) { + // 목표 달성을 위한产出가 최종 단계에 없음 + const hasGoalConnection = plan.steps.some(step => + step.produces?.some(p => plan.goal?.produces?.includes(p)) + ); + if (!hasGoalConnection) { + issues.push('어떤 단계도 목표 달성에 기여하지 않음'); + } + } + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 3: 실행 가능성 검증 + */ +export function validateExecutability(plan: Plan, availableCommands: string[]): ClauseResult { + const issues: string[] = []; + + if (!plan.steps) return { passed: false, issues: ['단계 없음'] }; + + for (const step of plan.steps) { + // 명령어 존재 확인 + if (step.command && !availableCommands.includes(step.command)) { + issues.push(`단계 ${step.id}: 명령어 "${step.command}" 사용 불가`); + } + + // 리소스 확인 + if (step.requiredResources) { + for (const resource of step.requiredResources) { + if (!availableCommands.includes(resource)) { + issues.push(`단계 ${step.id}: 필요한 리소스 "${resource}" 없음`); + } + } + } + + // 타임아웃 확인 + if (!step.timeout || step.timeout <= 0) { + issues.push(`단계 ${step.id}: 타임아웃 미설정`); + } + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 4: 리스크 평가 검증 + */ +export function validateRiskAssessment(plan: Plan): ClauseResult { + const issues: string[] = []; + + if (!plan.risks || plan.risks.length === 0) { + issues.push('리스크 식별 없음'); + } + + if (plan.risks) { + for (const risk of plan.risks) { + // 리스크에 대한 완화 전략 확인 + if (!risk.mitigation) { + issues.push(`리스크 "${risk.description}": 완화 전략 없음`); + } + + // 대안 경로 확인 (중간 리스크 이상) + if (risk.severity === 'high' || risk.severity === 'critical') { + if (!risk.alternativePath) { + issues.push(`고위험 리스크 "${risk.description}": 대안 경로 없음`); + } + } + } + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 5: 검증 가능성 검증 + */ +export function validateVerifiability(plan: Plan): ClauseResult { + const issues: string[] = []; + + if (!plan.steps) return { passed: false, issues: ['단계 없음'] }; + + for (const step of plan.steps) { + // 예상 결과 확인 + if (!step.expectedOutcome) { + issues.push(`단계 ${step.id}: 예상 결과 미정의`); + } + + // 어설션 확인 + if (!step.assertions || step.assertions.length === 0) { + issues.push(`단계 ${step.id}: 검증 어설션 없음`); + } + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 6: 시간적 제약 검증 + */ +export function validateTemporalConstraints(plan: Plan): ClauseResult { + const issues: string[] = []; + + // 전체 예상 소요 시간 확인 + if (!plan.estimatedDuration) { + issues.push('전체 예상 소요 시간 미정의'); + } + + // 각 단계 타임아웃 확인 + if (plan.steps) { + for (const step of plan.steps) { + if (!step.timeout || step.timeout <= 0) { + issues.push(`단계 ${step.id}: 타임아웃 없음`); + } + } + } + + // 시간 초과 시 대체 전략 확인 + if (plan.timeoutStrategy) { + issues.push('시간 초과 시 대체 전략 없음'); + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 조항 7: 롤백 가능성 검증 + */ +export function validateRollbackCapability(plan: Plan): ClauseResult { + const issues: string[] = []; + + // 롤백 명령 정의 확인 + if (!plan.rollbackCommands || plan.rollbackCommands.length === 0) { + issues.push('롤백 명령 미정의'); + } + + // 롤백 시퀀스 순서 확인 + if (plan.rollbackCommands) { + for (let i = 0; i < plan.rollbackCommands.length; i++) { + const cmd = plan.rollbackCommands[i]; + if (!cmd.order || cmd.order !== plan.rollbackCommands.length - i) { + issues.push(`롤백 명령 ${cmd.id}: 순서 미지정 또는 역순 아님`); + } + } + } + + // 롤백 성공 여부 검증 방법 확인 + if (plan.rollbackCommands) { + for (const cmd of plan.rollbackCommands) { + if (!cmd.verification) { + issues.push(`롤백 명령 ${cmd.id}: 성공 여부 검증 방법 없음`); + } + } + } + + return { + passed: issues.length === 0, + issues + }; +} + +/** + * 전체 계약 검증 + */ +export function validateContract( + plan: Plan, + availableCommands: string[] = [] +): { grade: ContractGrade; results: Record } { + const results: Record = { + goalClarity: validateGoalClarity(plan), + stepCompleteness: validateStepCompleteness(plan), + executability: validateExecutability(plan, availableCommands), + riskAssessment: validateRiskAssessment(plan), + verifiability: validateVerifiability(plan), + temporalConstraints: validateTemporalConstraints(plan), + rollbackCapability: validateRollbackCapability(plan) + }; + + const passedCount = Object.values(results).filter(r => r.passed).length; + + let grade: ContractGrade; + if (passedCount === 7) grade = 'A'; + else if (passedCount >= 5) grade = 'B'; + else if (passedCount >= 3) grade = 'C'; + else if (passedCount >= 1) grade = 'D'; + else grade = 'F'; + + return { grade, results }; +} + +/** + * 에피타이드 생성 + */ +export function createEpitaph( + plan: Plan, + validationResult: { grade: ContractGrade; results: Record }, + executionResult: { status: 'success' | 'partial' | 'failure'; duration: number; deviationFromPlan?: string } +): Epitaph { + return { + planId: plan.id, + timestamp: new Date().toISOString(), + grade: validationResult.grade, + clauses: validationResult.results, + executionResult + }; +} From 022ec6616c805981996e7dfe279268ae1f5079d1 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:17 +0000 Subject: [PATCH 4/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/types.ts | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 planning-contract/types.ts diff --git a/planning-contract/types.ts b/planning-contract/types.ts new file mode 100644 index 0000000..5b02c2e --- /dev/null +++ b/planning-contract/types.ts @@ -0,0 +1,82 @@ +/** + * 계획 계약 타입 정의 + */ + +/** 계약 등급 */ +export type ContractGrade = 'A' | 'B' | 'C' | 'D' | 'F'; + +/** 조항 검증 결과 */ +export interface ClauseResult { + passed: boolean; + issues: string[]; +} + +/** 목표 정의 */ +export interface Goal { + description: string; + criteria: string[]; + produces?: string[]; + subGoals?: string[]; +} + +/** 계획 단계 */ +export interface PlanStep { + id: string; + description: string; + command?: string; + dependencies?: string[]; + requiredResources?: string[]; + timeout?: number; + expectedOutcome?: string; + assertions?: string[]; + produces?: string[]; +} + +/** 리스크 정의 */ +export interface Risk { + id: string; + description: string; + severity: 'low' | 'medium' | 'high' | 'critical'; + mitigation?: string; + alternativePath?: string; +} + +/** 롤백 명령 */ +export interface RollbackCommand { + id: string; + command: string; + order: number; + verification?: string; +} + +/** 계획 */ +export interface Plan { + id: string; + goal: Goal; + steps: PlanStep[]; + risks?: Risk[]; + estimatedDuration?: number; + timeoutStrategy?: string; + rollbackCommands?: RollbackCommand[]; +} + +/** 에피타이드 (실행 결과 기록) */ +export interface Epitaph { + planId: string; + timestamp: string; + grade: ContractGrade; + clauses: { + goalClarity: ClauseResult; + stepCompleteness: ClauseResult; + executability: ClauseResult; + riskAssessment: ClauseResult; + verifiability: ClauseResult; + temporalConstraints: ClauseResult; + rollbackCapability: ClauseResult; + }; + executionResult: { + status: 'success' | 'partial' | 'failure'; + duration: number; + deviationFromPlan?: string | null; + }; +} From 43e4786a44fd10eb248ad7deb723447a0ff4aaa1 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:18 +0000 Subject: [PATCH 5/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/contract-validator.test.ts | 312 +++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 planning-contract/contract-validator.test.ts diff --git a/planning-contract/contract-validator.test.ts b/planning-contract/contract-validator.test.ts new file mode 100644 index 0000000..ed57a15 --- /dev/null +++ b/planning-contract/contract-validator.test.ts @@ -0,0 +1,312 @@ +/** + * 계획 계약 검증기 테스트 + */ + +import { + validateGoalClarity, + validateStepCompleteness, + validateExecutability, + validateRiskAssessment, + validateVerifiability, + validateTemporalConstraints, + validateRollbackCapability, + validateContract, + createEpitaph +} from './contract-validator'; +import { Plan } from './types'; + +describe('계획 계약 검증기', () => { + describe('조항 1: 목표 명확성', () => { + it('명확한 목표는 통과해야 함', () => { + const plan: Plan = { + id: 'test-1', + goal: { + description: '테스트 파일 10개 생성', + criteria: ['파일 10개 생성됨', '각 파일 크기 > 0'], + produces: ['test-files'] + }, + steps: [] + }; + + const result = validateGoalClarity(plan); + expect(result.passed).toBe(true); + expect(result.issues).toHaveLength(0); + }); + + it('모호한 표현이 있으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-2', + goal: { + description: '테스트 파일을 잘 생성', + criteria: [] + }, + steps: [] + }; + + const result = validateGoalClarity(plan); + expect(result.passed).toBe(false); + expect(result.issues.length).toBeGreaterThan(0); + }); + + it('판단 기준이 없으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-3', + goal: { + description: '파일 생성', + criteria: [] + }, + steps: [] + }; + + const result = validateGoalClarity(plan); + expect(result.passed).toBe(false); + expect(result.issues).toContain('목표 달성 판단 기준이 없음'); + }); + }); + + describe('조항 2: 단계 완전성', () => { + it('올바른 의존성 순서는 통과해야 함', () => { + const plan: Plan = { + id: 'test-4', + goal: { + description: '빌드 완료', + criteria: ['빌드 성공'], + produces: ['build-output'] + }, + steps: [ + { id: 'step1', description: '의존성 설치', produces: ['deps'] }, + { id: 'step2', description: '컴파일', dependencies: ['step1'], produces: ['build-output'] } + ] + }; + + const result = validateStepCompleteness(plan); + expect(result.passed).toBe(true); + }); + + it('순서 위반이 있으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-5', + goal: { + description: '빌드 완료', + criteria: ['빌드 성공'] + }, + steps: [ + { id: 'step1', description: '컴파일', dependencies: ['step2'] }, + { id: 'step2', description: '의존성 설치' } + ] + }; + + const result = validateStepCompleteness(plan); + expect(result.passed).toBe(false); + }); + }); + + describe('조항 3: 실행 가능성', () => { + it('사용 가능한 명령어는 통과해야 함', () => { + const plan: Plan = { + id: 'test-6', + goal: { description: '테스트', criteria: ['완료'] }, + steps: [ + { id: 'step1', command: 'npm', timeout: 30000 } + ] + }; + + const result = validateExecutability(plan, ['npm', 'node']); + expect(result.passed).toBe(true); + }); + + it('타임아웃 없으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-7', + goal: { description: '테스트', criteria: ['완료'] }, + steps: [ + { id: 'step1', command: 'npm' } + ] + }; + + const result = validateExecutability(plan, ['npm']); + expect(result.passed).toBe(false); + expect(result.issues).toContain('단계 step1: 타임아웃 미설정'); + }); + }); + + describe('조항 4: 리스크 평가', () => { + it('리스크와 완화 전략이 있으면 통과해야 함', () => { + const plan: Plan = { + id: 'test-8', + goal: { description: '배포', criteria: ['성공'] }, + steps: [], + risks: [ + { + id: 'risk1', + description: '네트워크 오류', + severity: 'medium', + mitigation: '재시도 로직 구현' + } + ] + }; + + const result = validateRiskAssessment(plan); + expect(result.passed).toBe(true); + }); + + it('고위험 리스크에 대안 경로 없으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-9', + goal: { description: '배포', criteria: ['성공'] }, + steps: [], + risks: [ + { + id: 'risk1', + description: '데이터 손상', + severity: 'critical' + } + ] + }; + + const result = validateRiskAssessment(plan); + expect(result.passed).toBe(false); + }); + }); + + describe('조항 5: 검증 가능성', () => { + it('예상 결과와 어설션이 있으면 통과해야 함', () => { + const plan: Plan = { + id: 'test-10', + goal: { description: '파일 생성', criteria: ['완료'] }, + steps: [ + { + id: 'step1', + description: '파일 생성', + expectedOutcome: '파일이 생성됨', + assertions: ['file.exists()'] + } + ] + }; + + const result = validateVerifiability(plan); + expect(result.passed).toBe(true); + }); + + it('어설션 없으면 실패해야 함', () => { + const plan: Plan = { + id: 'test-11', + goal: { description: '파일 생성', criteria: ['완료'] }, + steps: [ + { + id: 'step1', + description: '파일 생성', + expectedOutcome: '파일이 생성됨' + } + ] + }; + + const result = validateVerifiability(plan); + expect(result.passed).toBe(false); + }); + }); + + describe('조항 6: 시간적 제약', () => { + it('시간 제약이 설정되면 통과해야 함', () => { + const plan: Plan = { + id: 'test-12', + goal: { description: '빌드', criteria: ['완료'] }, + steps: [ + { id: 'step1', timeout: 60000 } + ], + estimatedDuration: 120000, + timeoutStrategy: '실패 후 롤백' + }; + + const result = validateTemporalConstraints(plan); + expect(result.passed).toBe(true); + }); + }); + + describe('조항 7: 롤백 가능성', () => { + it('올바른 롤백 시퀀스가 있으면 통과해야 함', () => { + const plan: Plan = { + id: 'test-13', + goal: { description: '배포', criteria: ['완료'] }, + steps: [], + rollbackCommands: [ + { id: 'rb1', command: 'rollback-step2', order: 2, verification: 'check-state' }, + { id: 'rb2', command: 'rollback-step1', order: 1, verification: 'check-state' } + ] + }; + + const result = validateRollbackCapability(plan); + expect(result.passed).toBe(true); + }); + }); + + describe('전체 계약 검증', () => { + it('모든 조항 충족 시 A 등급 부여', () => { + const plan: Plan = { + id: 'test-full', + goal: { + description: '완전한 테스트 계획', + criteria: ['모든 단계 성공'], + produces: ['result'] + }, + steps: [ + { + id: 'step1', + description: '준비', + command: 'prepare', + timeout: 10000, + expectedOutcome: '준비 완료', + assertions: ['ready'], + produces: ['prepared'] + }, + { + id: 'step2', + description: '실행', + command: 'execute', + dependencies: ['step1'], + timeout: 30000, + expectedOutcome: '실행 완료', + assertions: ['completed'], + produces: ['result'] + } + ], + risks: [ + { + id: 'risk1', + description: '실패 가능성', + severity: 'low', + mitigation: '재시도' + } + ], + estimatedDuration: 60000, + timeoutStrategy: '중단', + rollbackCommands: [ + { id: 'rb1', command: 'cleanup', order: 1, verification: 'check-clean' } + ] + }; + + const result = validateContract(plan, ['prepare', 'execute']); + expect(result.grade).toBe('A'); + }); + }); + + describe('에피타이드 생성', () => { + it('올바른 에피타이드를 생성해야 함', () => { + const plan: Plan = { + id: 'test-epitaph', + goal: { description: '테스트', criteria: ['완료'] }, + steps: [] + }; + + const validation = validateContract(plan); + const epitaph = createEpitaph(plan, validation, { + status: 'success', + duration: 5000 + }); + + expect(epitaph.planId).toBe('test-epitaph'); + expect(epitaph.timestamp).toBeDefined(); + expect(epitaph.executionResult.status).toBe('success'); + }); + }); +}); From b44d73c9e721bd8d7e694f9dd97c01c1ba68ddf7 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:19 +0000 Subject: [PATCH 6/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/package.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 planning-contract/package.json diff --git a/planning-contract/package.json b/planning-contract/package.json new file mode 100644 index 0000000..e7e6b16 --- /dev/null +++ b/planning-contract/package.json @@ -0,0 +1,24 @@ +{ + "name": "planning-contract", + "version": "1.0.0", + "description": "MiniMax 기반 에이전트의 계획 계약 명세서 및 검증기", + "main": "dist/contract-validator.js", + "types": "dist/types.d.ts", + "scripts": { + "build": "tsc", + "test": "jest", + "test:coverage": "jest --coverage" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "moduleFileExtensions": ["ts", "js", "json"], + "testMatch": ["**/*.test.ts"] + } +} From 968520261137ac9f3d7795348e14acc8102c5f47 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 10:14:20 +0000 Subject: [PATCH 7/7] =?UTF-8?q?=EA=B3=84=ED=9A=8D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=EC=84=9C=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EC=A4=80=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?(iss-6da9ba883259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planning-contract/tsconfig.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 planning-contract/tsconfig.json diff --git a/planning-contract/tsconfig.json b/planning-contract/tsconfig.json new file mode 100644 index 0000000..e98bfd6 --- /dev/null +++ b/planning-contract/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["*.ts"], + "exclude": ["node_modules", "dist"] +}