feat(persona): add role-based review personas
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 12s
CI / review (/anthropic/v1, anthropic--claude-4.6-sonnet, sonnet, anthropic, SONNET_REVIEW_TOKEN) (pull_request) Successful in 37s
CI / review (/openai/v1, gpt-5, gpt, openai, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m0s
CI / review (/openai/v1, gpt-5, security, openai, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m12s
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 12s
CI / review (/anthropic/v1, anthropic--claude-4.6-sonnet, sonnet, anthropic, SONNET_REVIEW_TOKEN) (pull_request) Successful in 37s
CI / review (/openai/v1, gpt-5, gpt, openai, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m0s
CI / review (/openai/v1, gpt-5, security, openai, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m12s
Add persona system for specialized review roles. Each persona defines: - A specific review focus (security, architecture, documentation) - Custom system prompt additions - Personality/tone adjustments Built-in personas: security, architect, docs Custom personas: load from JSON via persona-file flag Includes workspace validation to prevent path traversal attacks. Closes #51
This commit is contained in:
@@ -159,3 +159,58 @@ func TestFormatMarkdown_RoleTitle(t *testing.T) {
|
||||
t.Error("should not contain role title header when reviewer name is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatMarkdownWithDisplay(t *testing.T) {
|
||||
result := &ReviewResult{
|
||||
Verdict: "APPROVE",
|
||||
Summary: "Test summary",
|
||||
Findings: nil,
|
||||
Recommendation: "Test recommendation",
|
||||
}
|
||||
|
||||
t.Run("with display name", func(t *testing.T) {
|
||||
body := FormatMarkdownWithDisplay(result, "Security Specialist", "security")
|
||||
|
||||
// Header should use display name
|
||||
if !strings.Contains(body, "# Security Specialist Review") {
|
||||
t.Error("header should use display name")
|
||||
}
|
||||
|
||||
// Sentinel should use sentinel name
|
||||
if !strings.Contains(body, "<!-- review-bot:security -->") {
|
||||
t.Error("sentinel should use sentinel name")
|
||||
}
|
||||
|
||||
// Footer "Review by" should use display name
|
||||
if !strings.Contains(body, "*Review by Security Specialist*") {
|
||||
t.Error("footer should use display name")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("without display name", func(t *testing.T) {
|
||||
body := FormatMarkdownWithDisplay(result, "", "reviewer")
|
||||
|
||||
// Should fall back to sentinel name for header
|
||||
if !strings.Contains(body, "# Reviewer Review") {
|
||||
t.Error("header should fall back to sentinel name")
|
||||
}
|
||||
|
||||
if !strings.Contains(body, "<!-- review-bot:reviewer -->") {
|
||||
t.Error("sentinel should use sentinel name")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty both names", func(t *testing.T) {
|
||||
body := FormatMarkdownWithDisplay(result, "", "")
|
||||
|
||||
// Should not have header
|
||||
if strings.Contains(body, "# ") && strings.Contains(body, " Review") {
|
||||
t.Error("should not have header when both names empty")
|
||||
}
|
||||
|
||||
// Should not have sentinel
|
||||
if strings.Contains(body, "<!-- review-bot:") {
|
||||
t.Error("should not have sentinel when sentinel name empty")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user