Add compacted_at_commit field and git commit capture during compaction
- Add compacted_at_commit field to Issue type (bd-405) - Add database schema and migration for new field - Create GetCurrentCommitHash() helper function - Update ApplyCompaction to store git commit hash (bd-395) - Update compaction calls to capture current commit - Update tests to verify commit hash storage - All tests passing Amp-Thread-ID: https://ampcode.com/threads/T-5518cccb-7fc9-4dcd-ba5a-e22cd10e45d7 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -279,8 +279,8 @@ func (s *SQLiteStorage) CheckEligibility(ctx context.Context, issueID string, ti
|
||||
}
|
||||
|
||||
// ApplyCompaction updates the compaction metadata for an issue after successfully compacting it.
|
||||
// This sets compaction_level, compacted_at, and original_size fields.
|
||||
func (s *SQLiteStorage) ApplyCompaction(ctx context.Context, issueID string, level int, originalSize int, compressedSize int) error {
|
||||
// This sets compaction_level, compacted_at, compacted_at_commit, and original_size fields.
|
||||
func (s *SQLiteStorage) ApplyCompaction(ctx context.Context, issueID string, level int, originalSize int, compressedSize int, commitHash string) error {
|
||||
now := time.Now().UTC()
|
||||
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
@@ -289,14 +289,20 @@ func (s *SQLiteStorage) ApplyCompaction(ctx context.Context, issueID string, lev
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
var commitHashPtr *string
|
||||
if commitHash != "" {
|
||||
commitHashPtr = &commitHash
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
UPDATE issues
|
||||
SET compaction_level = ?,
|
||||
compacted_at = ?,
|
||||
compacted_at_commit = ?,
|
||||
original_size = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ?
|
||||
`, level, now, originalSize, now, issueID)
|
||||
`, level, now, commitHashPtr, originalSize, now, issueID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply compaction metadata: %w", err)
|
||||
|
||||
@@ -333,18 +333,19 @@ func TestApplyCompaction(t *testing.T) {
|
||||
}
|
||||
|
||||
originalSize := len(issue.Description)
|
||||
err := store.ApplyCompaction(ctx, issue.ID, 1, originalSize, 500)
|
||||
err := store.ApplyCompaction(ctx, issue.ID, 1, originalSize, 500, "abc123")
|
||||
if err != nil {
|
||||
t.Fatalf("ApplyCompaction failed: %v", err)
|
||||
}
|
||||
|
||||
var compactionLevel int
|
||||
var compactedAt sql.NullTime
|
||||
var compactedAtCommit sql.NullString
|
||||
var storedSize int
|
||||
err = store.db.QueryRowContext(ctx, `
|
||||
SELECT COALESCE(compaction_level, 0), compacted_at, COALESCE(original_size, 0)
|
||||
SELECT COALESCE(compaction_level, 0), compacted_at, compacted_at_commit, COALESCE(original_size, 0)
|
||||
FROM issues WHERE id = ?
|
||||
`, issue.ID).Scan(&compactionLevel, &compactedAt, &storedSize)
|
||||
`, issue.ID).Scan(&compactionLevel, &compactedAt, &compactedAtCommit, &storedSize)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to query issue: %v", err)
|
||||
}
|
||||
@@ -355,6 +356,9 @@ func TestApplyCompaction(t *testing.T) {
|
||||
if !compactedAt.Valid {
|
||||
t.Error("Expected compacted_at to be set")
|
||||
}
|
||||
if !compactedAtCommit.Valid || compactedAtCommit.String != "abc123" {
|
||||
t.Errorf("Expected compacted_at_commit 'abc123', got %v", compactedAtCommit)
|
||||
}
|
||||
if storedSize != originalSize {
|
||||
t.Errorf("Expected original_size %d, got %d", originalSize, storedSize)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS issues (
|
||||
external_ref TEXT,
|
||||
compaction_level INTEGER DEFAULT 0,
|
||||
compacted_at DATETIME,
|
||||
compacted_at_commit TEXT,
|
||||
original_size INTEGER,
|
||||
CHECK ((status = 'closed') = (closed_at IS NOT NULL))
|
||||
);
|
||||
|
||||
@@ -87,6 +87,11 @@ func New(path string) (*SQLiteStorage, error) {
|
||||
return nil, fmt.Errorf("failed to migrate compaction config: %w", err)
|
||||
}
|
||||
|
||||
// Migrate existing databases to add compacted_at_commit column
|
||||
if err := migrateCompactedAtCommitColumn(db); err != nil {
|
||||
return nil, fmt.Errorf("failed to migrate compacted_at_commit column: %w", err)
|
||||
}
|
||||
|
||||
return &SQLiteStorage{
|
||||
db: db,
|
||||
}, nil
|
||||
@@ -401,6 +406,31 @@ func migrateCompactionConfig(db *sql.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateCompactedAtCommitColumn adds compacted_at_commit column to the issues table.
|
||||
// This migration is idempotent and safe to run multiple times.
|
||||
func migrateCompactedAtCommitColumn(db *sql.DB) error {
|
||||
var columnExists bool
|
||||
err := db.QueryRow(`
|
||||
SELECT COUNT(*) > 0
|
||||
FROM pragma_table_info('issues')
|
||||
WHERE name = 'compacted_at_commit'
|
||||
`).Scan(&columnExists)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check compacted_at_commit column: %w", err)
|
||||
}
|
||||
|
||||
if columnExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = db.Exec(`ALTER TABLE issues ADD COLUMN compacted_at_commit TEXT`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add compacted_at_commit column: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getNextIDForPrefix atomically generates the next ID for a given prefix
|
||||
// Uses the issue_counters table for atomic, cross-process ID generation
|
||||
func (s *SQLiteStorage) getNextIDForPrefix(ctx context.Context, prefix string) (int, error) {
|
||||
@@ -846,11 +876,12 @@ func (s *SQLiteStorage) GetIssue(ctx context.Context, id string) (*types.Issue,
|
||||
var compactedAt sql.NullTime
|
||||
var originalSize sql.NullInt64
|
||||
|
||||
var compactedAtCommit sql.NullString
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT id, title, description, design, acceptance_criteria, notes,
|
||||
status, priority, issue_type, assignee, estimated_minutes,
|
||||
created_at, updated_at, closed_at, external_ref,
|
||||
compaction_level, compacted_at, original_size
|
||||
compaction_level, compacted_at, compacted_at_commit, original_size
|
||||
FROM issues
|
||||
WHERE id = ?
|
||||
`, id).Scan(
|
||||
@@ -858,7 +889,7 @@ func (s *SQLiteStorage) GetIssue(ctx context.Context, id string) (*types.Issue,
|
||||
&issue.AcceptanceCriteria, &issue.Notes, &issue.Status,
|
||||
&issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes,
|
||||
&issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef,
|
||||
&issue.CompactionLevel, &compactedAt, &originalSize,
|
||||
&issue.CompactionLevel, &compactedAt, &compactedAtCommit, &originalSize,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -884,6 +915,9 @@ func (s *SQLiteStorage) GetIssue(ctx context.Context, id string) (*types.Issue,
|
||||
if compactedAt.Valid {
|
||||
issue.CompactedAt = &compactedAt.Time
|
||||
}
|
||||
if compactedAtCommit.Valid {
|
||||
issue.CompactedAtCommit = &compactedAtCommit.String
|
||||
}
|
||||
if originalSize.Valid {
|
||||
issue.OriginalSize = int(originalSize.Int64)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user