Remove unreachable utility functions (bd-224, bd-214)
- Remove duplicate computeIssueContentHash from sqlite/hash.go - Remove FileUsed() from internal/config/config.go - Remove verifyIssueOpen() test helper from git_sync_test.go - Remove unimplemented SummarizeTier2 and all tier2 infrastructure from haiku.go Removes ~120 LOC of dead code identified by deadcode analyzer. Amp-Thread-ID: https://ampcode.com/threads/T-5f150c35-8d67-4dae-bb92-a7b5887d649d Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -296,13 +296,3 @@ func verifyIssueClosed(t *testing.T, store *sqlite.SQLiteStorage, issueID string
|
|||||||
t.Errorf("Expected issue %s to be closed, got status %s", issueID, issue.Status)
|
t.Errorf("Expected issue %s to be closed, got status %s", issueID, issue.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyIssueOpen(t *testing.T, store *sqlite.SQLiteStorage, issueID string) {
|
|
||||||
issue, err := store.GetIssue(context.Background(), issueID)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to get issue %s: %v", issueID, err)
|
|
||||||
}
|
|
||||||
if issue.Status != types.StatusOpen {
|
|
||||||
t.Errorf("Expected issue %s to be open, got status %s", issueID, issue.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ type HaikuClient struct {
|
|||||||
client anthropic.Client
|
client anthropic.Client
|
||||||
model anthropic.Model
|
model anthropic.Model
|
||||||
tier1Template *template.Template
|
tier1Template *template.Template
|
||||||
tier2Template *template.Template
|
|
||||||
maxRetries int
|
maxRetries int
|
||||||
initialBackoff time.Duration
|
initialBackoff time.Duration
|
||||||
}
|
}
|
||||||
@@ -52,16 +51,10 @@ func NewHaikuClient(apiKey string) (*HaikuClient, error) {
|
|||||||
return nil, fmt.Errorf("failed to parse tier1 template: %w", err)
|
return nil, fmt.Errorf("failed to parse tier1 template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tier2Tmpl, err := template.New("tier2").Parse(tier2PromptTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse tier2 template: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &HaikuClient{
|
return &HaikuClient{
|
||||||
client: client,
|
client: client,
|
||||||
model: defaultModel,
|
model: defaultModel,
|
||||||
tier1Template: tier1Tmpl,
|
tier1Template: tier1Tmpl,
|
||||||
tier2Template: tier2Tmpl,
|
|
||||||
maxRetries: maxRetries,
|
maxRetries: maxRetries,
|
||||||
initialBackoff: initialBackoff,
|
initialBackoff: initialBackoff,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -77,16 +70,6 @@ func (h *HaikuClient) SummarizeTier1(ctx context.Context, issue *types.Issue) (s
|
|||||||
return h.callWithRetry(ctx, prompt)
|
return h.callWithRetry(ctx, prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SummarizeTier2 creates an ultra-compressed single-paragraph summary (≤150 words).
|
|
||||||
func (h *HaikuClient) SummarizeTier2(ctx context.Context, issue *types.Issue) (string, error) {
|
|
||||||
prompt, err := h.renderTier2Prompt(issue)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to render prompt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.callWithRetry(ctx, prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HaikuClient) callWithRetry(ctx context.Context, prompt string) (string, error) {
|
func (h *HaikuClient) callWithRetry(ctx context.Context, prompt string) (string, error) {
|
||||||
var lastErr error
|
var lastErr error
|
||||||
params := anthropic.MessageNewParams{
|
params := anthropic.MessageNewParams{
|
||||||
@@ -186,26 +169,6 @@ func (h *HaikuClient) renderTier1Prompt(issue *types.Issue) (string, error) {
|
|||||||
return string(w.buf), nil
|
return string(w.buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type tier2Data struct {
|
|
||||||
Title string
|
|
||||||
CurrentDescription string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HaikuClient) renderTier2Prompt(issue *types.Issue) (string, error) {
|
|
||||||
var buf []byte
|
|
||||||
w := &bytesWriter{buf: buf}
|
|
||||||
|
|
||||||
data := tier2Data{
|
|
||||||
Title: issue.Title,
|
|
||||||
CurrentDescription: issue.Description,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := h.tier2Template.Execute(w, data); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(w.buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type bytesWriter struct {
|
type bytesWriter struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
@@ -243,17 +206,3 @@ Provide a summary in this exact format:
|
|||||||
**Key Decisions:** [Brief bullet points of only the most important technical choices]
|
**Key Decisions:** [Brief bullet points of only the most important technical choices]
|
||||||
|
|
||||||
**Resolution:** [One sentence on final outcome and lasting impact]`
|
**Resolution:** [One sentence on final outcome and lasting impact]`
|
||||||
|
|
||||||
const tier2PromptTemplate = `You are performing ultra-compression on a closed software issue. The issue has already been summarized once. Your task is to create a single concise paragraph (≤150 words) that captures the essence.
|
|
||||||
|
|
||||||
**Title:** {{.Title}}
|
|
||||||
|
|
||||||
**Current Summary:**
|
|
||||||
{{.CurrentDescription}}
|
|
||||||
|
|
||||||
Provide a single paragraph that covers:
|
|
||||||
- What was built/fixed
|
|
||||||
- Why it mattered
|
|
||||||
- Any lasting impact or decisions
|
|
||||||
|
|
||||||
Keep it under 150 words while retaining the most important context.`
|
|
||||||
|
|||||||
@@ -90,38 +90,6 @@ func TestRenderTier1Prompt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderTier2Prompt(t *testing.T) {
|
|
||||||
client, err := NewHaikuClient("test-key")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
issue := &types.Issue{
|
|
||||||
ID: "bd-1",
|
|
||||||
Title: "Fix authentication bug",
|
|
||||||
Description: "**Summary:** Fixed OAuth login flow by adding proper error handling.",
|
|
||||||
Status: types.StatusClosed,
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt, err := client.renderTier2Prompt(issue)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to render prompt: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(prompt, "Fix authentication bug") {
|
|
||||||
t.Error("prompt should contain title")
|
|
||||||
}
|
|
||||||
if !strings.Contains(prompt, "Fixed OAuth login flow") {
|
|
||||||
t.Error("prompt should contain current description")
|
|
||||||
}
|
|
||||||
if !strings.Contains(prompt, "150 words") {
|
|
||||||
t.Error("prompt should contain word limit")
|
|
||||||
}
|
|
||||||
if !strings.Contains(prompt, "ultra-compression") {
|
|
||||||
t.Error("prompt should indicate ultra-compression")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRenderTier1Prompt_HandlesEmptyFields(t *testing.T) {
|
func TestRenderTier1Prompt_HandlesEmptyFields(t *testing.T) {
|
||||||
client, err := NewHaikuClient("test-key")
|
client, err := NewHaikuClient("test-key")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -147,14 +147,6 @@ func Set(key string, value interface{}) {
|
|||||||
// return v.BindPFlag(key, flag)
|
// return v.BindPFlag(key, flag)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// FileUsed returns the path to the active configuration file.
|
|
||||||
func FileUsed() string {
|
|
||||||
if v == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return v.ConfigFileUsed()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllSettings returns all configuration settings as a map
|
// AllSettings returns all configuration settings as a map
|
||||||
func AllSettings() map[string]interface{} {
|
func AllSettings() map[string]interface{} {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
|||||||
@@ -2,41 +2,10 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// computeIssueContentHash computes a SHA256 hash of an issue's content, excluding timestamps.
|
|
||||||
// This is used for detecting timestamp-only changes during export deduplication.
|
|
||||||
func computeIssueContentHash(issue *types.Issue) (string, error) {
|
|
||||||
// Clone issue and zero out timestamps to exclude them from hash
|
|
||||||
normalized := *issue
|
|
||||||
normalized.CreatedAt = time.Time{}
|
|
||||||
normalized.UpdatedAt = time.Time{}
|
|
||||||
|
|
||||||
// Also zero out ClosedAt if present
|
|
||||||
if normalized.ClosedAt != nil {
|
|
||||||
zeroTime := time.Time{}
|
|
||||||
normalized.ClosedAt = &zeroTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize to JSON
|
|
||||||
data, err := json.Marshal(normalized)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHA256 hash
|
|
||||||
hash := sha256.Sum256(data)
|
|
||||||
return hex.EncodeToString(hash[:]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExportHash retrieves the content hash of the last export for an issue.
|
// GetExportHash retrieves the content hash of the last export for an issue.
|
||||||
// Returns empty string if no hash is stored (first export).
|
// Returns empty string if no hash is stored (first export).
|
||||||
func (s *SQLiteStorage) GetExportHash(ctx context.Context, issueID string) (string, error) {
|
func (s *SQLiteStorage) GetExportHash(ctx context.Context, issueID string) (string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user