From c3ef1c3f382ac2485a9b793051a291937b4ed37c Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Fri, 26 Dec 2025 13:33:54 -0800 Subject: [PATCH] feat: Add created_by field to issues (GH#748) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a created_by field to track who created each issue, similar to how comments have an author field. - Add CreatedBy string field to Issue struct - Add migration 029 to add created_by column to issues table - Update all SELECT/INSERT/Scan statements across storage layer - Populate created_by in bd create from actor chain - Display created_by in bd show output 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/create.go | 1 + cmd/bd/create_form.go | 1 + cmd/bd/show.go | 6 ++++ internal/storage/sqlite/dependencies.go | 16 ++++----- internal/storage/sqlite/issues.go | 12 +++---- internal/storage/sqlite/labels.go | 2 +- internal/storage/sqlite/migrations.go | 1 + .../migrations/029_created_by_column.go | 34 +++++++++++++++++++ internal/storage/sqlite/migrations_test.go | 3 +- internal/storage/sqlite/queries.go | 10 +++--- internal/storage/sqlite/ready.go | 8 ++--- internal/storage/sqlite/schema.go | 1 + internal/storage/sqlite/transaction.go | 6 ++-- internal/types/types.go | 5 ++- 14 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 internal/storage/sqlite/migrations/029_created_by_column.go diff --git a/cmd/bd/create.go b/cmd/bd/create.go index a8f535c8..e097b065 100644 --- a/cmd/bd/create.go +++ b/cmd/bd/create.go @@ -268,6 +268,7 @@ var createCmd = &cobra.Command{ ExternalRef: externalRefPtr, EstimatedMinutes: estimatedMinutes, Wisp: wisp, + CreatedBy: getActorWithGit(), // GH#748: track who created the issue } ctx := rootCtx diff --git a/cmd/bd/create_form.go b/cmd/bd/create_form.go index d246400d..f7b5a88c 100644 --- a/cmd/bd/create_form.go +++ b/cmd/bd/create_form.go @@ -111,6 +111,7 @@ func CreateIssueFromFormValues(ctx context.Context, s storage.Storage, fv *creat IssueType: types.IssueType(fv.IssueType), Assignee: fv.Assignee, ExternalRef: externalRefPtr, + CreatedBy: getActorWithGit(), // GH#748: track who created the issue } // Check if any dependencies are discovered-from type diff --git a/cmd/bd/show.go b/cmd/bd/show.go index 61e9203b..ac17541b 100644 --- a/cmd/bd/show.go +++ b/cmd/bd/show.go @@ -140,6 +140,9 @@ var showCmd = &cobra.Command{ fmt.Printf("Estimated: %d minutes\n", *issue.EstimatedMinutes) } fmt.Printf("Created: %s\n", issue.CreatedAt.Format("2006-01-02 15:04")) + if issue.CreatedBy != "" { + fmt.Printf("Created by: %s\n", issue.CreatedBy) + } fmt.Printf("Updated: %s\n", issue.UpdatedAt.Format("2006-01-02 15:04")) // Show compaction status @@ -319,6 +322,9 @@ var showCmd = &cobra.Command{ fmt.Printf("Estimated: %d minutes\n", *issue.EstimatedMinutes) } fmt.Printf("Created: %s\n", issue.CreatedAt.Format("2006-01-02 15:04")) + if issue.CreatedBy != "" { + fmt.Printf("Created by: %s\n", issue.CreatedBy) + } fmt.Printf("Updated: %s\n", issue.UpdatedAt.Format("2006-01-02 15:04")) // Show compaction status footer diff --git a/internal/storage/sqlite/dependencies.go b/internal/storage/sqlite/dependencies.go index 42df2697..282da900 100644 --- a/internal/storage/sqlite/dependencies.go +++ b/internal/storage/sqlite/dependencies.go @@ -247,7 +247,7 @@ func (s *SQLiteStorage) GetDependenciesWithMetadata(ctx context.Context, issueID rows, err := s.db.QueryContext(ctx, ` SELECT i.id, i.content_hash, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.deleted_at, i.deleted_by, i.delete_reason, i.original_type, i.sender, i.ephemeral, i.pinned, i.is_template, i.await_type, i.await_id, i.timeout_ns, i.waiters, @@ -270,7 +270,7 @@ func (s *SQLiteStorage) GetDependentsWithMetadata(ctx context.Context, issueID s rows, err := s.db.QueryContext(ctx, ` SELECT i.id, i.content_hash, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.deleted_at, i.deleted_by, i.delete_reason, i.original_type, i.sender, i.ephemeral, i.pinned, i.is_template, i.await_type, i.await_id, i.timeout_ns, i.waiters, @@ -484,7 +484,7 @@ func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, m SELECT i.id, i.title, i.status, i.priority, i.description, i.design, i.acceptance_criteria, i.notes, i.issue_type, i.assignee, - i.estimated_minutes, i.created_at, i.updated_at, i.closed_at, + i.estimated_minutes, i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, 0 as depth, i.id as path, @@ -497,7 +497,7 @@ func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, m SELECT i.id, i.title, i.status, i.priority, i.description, i.design, i.acceptance_criteria, i.notes, i.issue_type, i.assignee, - i.estimated_minutes, i.created_at, i.updated_at, i.closed_at, + i.estimated_minutes, i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, t.depth + 1, t.path || '→' || i.id, @@ -525,7 +525,7 @@ func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, m SELECT i.id, i.title, i.status, i.priority, i.description, i.design, i.acceptance_criteria, i.notes, i.issue_type, i.assignee, - i.estimated_minutes, i.created_at, i.updated_at, i.closed_at, + i.estimated_minutes, i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, 0 as depth, i.id as path, @@ -538,7 +538,7 @@ func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, m SELECT i.id, i.title, i.status, i.priority, i.description, i.design, i.acceptance_criteria, i.notes, i.issue_type, i.assignee, - i.estimated_minutes, i.created_at, i.updated_at, i.closed_at, + i.estimated_minutes, i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, t.depth + 1, t.path || '→' || i.id, @@ -839,7 +839,7 @@ func (s *SQLiteStorage) scanIssues(ctx context.Context, rows *sql.Rows) ([]*type &issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, &closeReason, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, &closeReason, &deletedAt, &deletedBy, &deleteReason, &originalType, &sender, &wisp, &pinned, &isTemplate, &awaitType, &awaitID, &timeoutNs, &waiters, @@ -962,7 +962,7 @@ func (s *SQLiteStorage) scanIssuesWithDependencyType(ctx context.Context, rows * &issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, &deletedAt, &deletedBy, &deleteReason, &originalType, &sender, &wisp, &pinned, &isTemplate, &awaitType, &awaitID, &timeoutNs, &waiters, diff --git a/internal/storage/sqlite/issues.go b/internal/storage/sqlite/issues.go index 7c0a9166..41d221f3 100644 --- a/internal/storage/sqlite/issues.go +++ b/internal/storage/sqlite/issues.go @@ -44,16 +44,16 @@ func insertIssue(ctx context.Context, conn *sql.Conn, issue *types.Issue) error INSERT OR IGNORE INTO issues ( id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, source_repo, close_reason, + created_at, created_by, updated_at, closed_at, external_ref, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, await_type, await_id, timeout_ns, waiters - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, issue.ID, issue.ContentHash, issue.Title, issue.Description, issue.Design, issue.AcceptanceCriteria, issue.Notes, issue.Status, issue.Priority, issue.IssueType, issue.Assignee, - issue.EstimatedMinutes, issue.CreatedAt, issue.UpdatedAt, + issue.EstimatedMinutes, issue.CreatedAt, issue.CreatedBy, issue.UpdatedAt, issue.ClosedAt, issue.ExternalRef, sourceRepo, issue.CloseReason, issue.DeletedAt, issue.DeletedBy, issue.DeleteReason, issue.OriginalType, issue.Sender, wisp, pinned, isTemplate, @@ -76,11 +76,11 @@ func insertIssues(ctx context.Context, conn *sql.Conn, issues []*types.Issue) er INSERT OR IGNORE INTO issues ( id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, source_repo, close_reason, + created_at, created_by, updated_at, closed_at, external_ref, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, await_type, await_id, timeout_ns, waiters - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `) if err != nil { return fmt.Errorf("failed to prepare statement: %w", err) @@ -110,7 +110,7 @@ func insertIssues(ctx context.Context, conn *sql.Conn, issues []*types.Issue) er issue.ID, issue.ContentHash, issue.Title, issue.Description, issue.Design, issue.AcceptanceCriteria, issue.Notes, issue.Status, issue.Priority, issue.IssueType, issue.Assignee, - issue.EstimatedMinutes, issue.CreatedAt, issue.UpdatedAt, + issue.EstimatedMinutes, issue.CreatedAt, issue.CreatedBy, issue.UpdatedAt, issue.ClosedAt, issue.ExternalRef, sourceRepo, issue.CloseReason, issue.DeletedAt, issue.DeletedBy, issue.DeleteReason, issue.OriginalType, issue.Sender, wisp, pinned, isTemplate, diff --git a/internal/storage/sqlite/labels.go b/internal/storage/sqlite/labels.go index f70a3d2e..baa1d035 100644 --- a/internal/storage/sqlite/labels.go +++ b/internal/storage/sqlite/labels.go @@ -157,7 +157,7 @@ func (s *SQLiteStorage) GetIssuesByLabel(ctx context.Context, label string) ([]* rows, err := s.db.QueryContext(ctx, ` SELECT i.id, i.content_hash, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, i.deleted_at, i.deleted_by, i.delete_reason, i.original_type, i.sender, i.ephemeral, i.pinned, i.is_template, i.await_type, i.await_id, i.timeout_ns, i.waiters diff --git a/internal/storage/sqlite/migrations.go b/internal/storage/sqlite/migrations.go index d982f27c..de2409f5 100644 --- a/internal/storage/sqlite/migrations.go +++ b/internal/storage/sqlite/migrations.go @@ -45,6 +45,7 @@ var migrationsList = []Migration{ {"additional_indexes", migrations.MigrateAdditionalIndexes}, {"gate_columns", migrations.MigrateGateColumns}, {"tombstone_closed_at", migrations.MigrateTombstoneClosedAt}, + {"created_by_column", migrations.MigrateCreatedByColumn}, } // MigrationInfo contains metadata about a migration for inspection diff --git a/internal/storage/sqlite/migrations/029_created_by_column.go b/internal/storage/sqlite/migrations/029_created_by_column.go new file mode 100644 index 00000000..c07f5392 --- /dev/null +++ b/internal/storage/sqlite/migrations/029_created_by_column.go @@ -0,0 +1,34 @@ +package migrations + +import ( + "database/sql" + "fmt" +) + +// MigrateCreatedByColumn adds the created_by column to the issues table. +// This tracks who created the issue, using the same actor chain as comment authors +// (--actor flag, BD_ACTOR env, or $USER). GH#748. +func MigrateCreatedByColumn(db *sql.DB) error { + // Check if column already exists + var columnExists bool + err := db.QueryRow(` + SELECT COUNT(*) > 0 + FROM pragma_table_info('issues') + WHERE name = 'created_by' + `).Scan(&columnExists) + if err != nil { + return fmt.Errorf("failed to check created_by column: %w", err) + } + + if columnExists { + return nil + } + + // Add the created_by column + _, err = db.Exec(`ALTER TABLE issues ADD COLUMN created_by TEXT DEFAULT ''`) + if err != nil { + return fmt.Errorf("failed to add created_by column: %w", err) + } + + return nil +} diff --git a/internal/storage/sqlite/migrations_test.go b/internal/storage/sqlite/migrations_test.go index 71df3913..c1175dc6 100644 --- a/internal/storage/sqlite/migrations_test.go +++ b/internal/storage/sqlite/migrations_test.go @@ -470,6 +470,7 @@ func TestMigrateContentHashColumn(t *testing.T) { assignee TEXT, estimated_minutes INTEGER, created_at DATETIME NOT NULL, + created_by TEXT DEFAULT '', updated_at DATETIME NOT NULL, closed_at DATETIME, external_ref TEXT, @@ -497,7 +498,7 @@ func TestMigrateContentHashColumn(t *testing.T) { waiters TEXT DEFAULT '', CHECK ((status = 'closed') = (closed_at IS NOT NULL)) ); - INSERT INTO issues 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, compacted_at_commit, source_repo, '', NULL, '', '', '', '', 0, 0, 0, '', '', '', '', '', '', 0, '' FROM issues_backup; + INSERT INTO issues 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, compacted_at_commit, source_repo, '', NULL, '', '', '', '', 0, 0, 0, '', '', '', '', '', '', 0, '' FROM issues_backup; DROP TABLE issues_backup; `) if err != nil { diff --git a/internal/storage/sqlite/queries.go b/internal/storage/sqlite/queries.go index 6a55f5aa..07325aaf 100644 --- a/internal/storage/sqlite/queries.go +++ b/internal/storage/sqlite/queries.go @@ -278,7 +278,7 @@ func (s *SQLiteStorage) GetIssue(ctx context.Context, id string) (*types.Issue, err := s.db.QueryRowContext(ctx, ` SELECT id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, + created_at, created_by, updated_at, closed_at, external_ref, compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, @@ -289,7 +289,7 @@ func (s *SQLiteStorage) GetIssue(ctx context.Context, id string) (*types.Issue, &issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRef, &issue.CompactionLevel, &compactedAt, &compactedAtCommit, &originalSize, &sourceRepo, &closeReason, &deletedAt, &deletedBy, &deleteReason, &originalType, &sender, &wisp, &pinned, &isTemplate, @@ -491,7 +491,7 @@ func (s *SQLiteStorage) GetIssueByExternalRef(ctx context.Context, externalRef s err := s.db.QueryRowContext(ctx, ` SELECT id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, + created_at, created_by, updated_at, closed_at, external_ref, compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, @@ -502,7 +502,7 @@ func (s *SQLiteStorage) GetIssueByExternalRef(ctx context.Context, externalRef s &issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRefCol, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRefCol, &issue.CompactionLevel, &compactedAt, &compactedAtCommit, &originalSize, &sourceRepo, &closeReason, &deletedAt, &deletedBy, &deleteReason, &originalType, &sender, &wisp, &pinned, &isTemplate, @@ -1699,7 +1699,7 @@ func (s *SQLiteStorage) SearchIssues(ctx context.Context, query string, filter t querySQL := fmt.Sprintf(` SELECT id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, source_repo, close_reason, + created_at, created_by, updated_at, closed_at, external_ref, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, await_type, await_id, timeout_ns, waiters diff --git a/internal/storage/sqlite/ready.go b/internal/storage/sqlite/ready.go index 23e22a38..29604142 100644 --- a/internal/storage/sqlite/ready.go +++ b/internal/storage/sqlite/ready.go @@ -137,7 +137,7 @@ func (s *SQLiteStorage) GetReadyWork(ctx context.Context, filter types.WorkFilte query := fmt.Sprintf(` SELECT i.id, i.content_hash, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, i.deleted_at, i.deleted_by, i.delete_reason, i.original_type, i.sender, i.ephemeral, i.pinned, i.is_template, i.await_type, i.await_id, i.timeout_ns, i.waiters @@ -476,7 +476,7 @@ func (s *SQLiteStorage) GetBlockedIssues(ctx context.Context, filter types.WorkF SELECT i.id, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, COALESCE(COUNT(d.depends_on_id), 0) as blocked_by_count, COALESCE(GROUP_CONCAT(d.depends_on_id, ','), '') as blocker_ids FROM issues i @@ -537,7 +537,7 @@ func (s *SQLiteStorage) GetBlockedIssues(ctx context.Context, filter types.WorkF &issue.ID, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, &issue.BlockedByCount, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo, &issue.BlockedByCount, &blockerIDsStr, ) if err != nil { @@ -665,7 +665,7 @@ func (s *SQLiteStorage) GetNewlyUnblockedByClose(ctx context.Context, closedIssu query := ` SELECT i.id, i.content_hash, i.title, i.description, i.design, i.acceptance_criteria, i.notes, i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes, - i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, + i.created_at, i.created_by, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason, i.deleted_at, i.deleted_by, i.delete_reason, i.original_type, i.sender, i.ephemeral, i.pinned, i.is_template, i.await_type, i.await_id, i.timeout_ns, i.waiters diff --git a/internal/storage/sqlite/schema.go b/internal/storage/sqlite/schema.go index fb2c7203..898b13f4 100644 --- a/internal/storage/sqlite/schema.go +++ b/internal/storage/sqlite/schema.go @@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS issues ( assignee TEXT, estimated_minutes INTEGER, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by TEXT DEFAULT '', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, closed_at DATETIME, external_ref TEXT, diff --git a/internal/storage/sqlite/transaction.go b/internal/storage/sqlite/transaction.go index f854545e..b7e4067b 100644 --- a/internal/storage/sqlite/transaction.go +++ b/internal/storage/sqlite/transaction.go @@ -310,7 +310,7 @@ func (t *sqliteTxStorage) GetIssue(ctx context.Context, id string) (*types.Issue row := t.conn.QueryRowContext(ctx, ` SELECT id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, + created_at, created_by, updated_at, closed_at, external_ref, compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, @@ -1127,7 +1127,7 @@ func (t *sqliteTxStorage) SearchIssues(ctx context.Context, query string, filter querySQL := fmt.Sprintf(` SELECT id, content_hash, title, description, design, acceptance_criteria, notes, status, priority, issue_type, assignee, estimated_minutes, - created_at, updated_at, closed_at, external_ref, + created_at, created_by, updated_at, closed_at, external_ref, compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason, deleted_at, deleted_by, delete_reason, original_type, sender, ephemeral, pinned, is_template, @@ -1188,7 +1188,7 @@ func scanIssueRow(row scanner) (*types.Issue, error) { &issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design, &issue.AcceptanceCriteria, &issue.Notes, &issue.Status, &issue.Priority, &issue.IssueType, &assignee, &estimatedMinutes, - &issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, + &issue.CreatedAt, &issue.CreatedBy, &issue.UpdatedAt, &closedAt, &externalRef, &issue.CompactionLevel, &compactedAt, &compactedAtCommit, &originalSize, &sourceRepo, &closeReason, &deletedAt, &deletedBy, &deleteReason, &originalType, &sender, &wisp, &pinned, &isTemplate, diff --git a/internal/types/types.go b/internal/types/types.go index f7f637dc..105ca94b 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -23,6 +23,7 @@ type Issue struct { Assignee string `json:"assignee,omitempty"` EstimatedMinutes *int `json:"estimated_minutes,omitempty"` CreatedAt time.Time `json:"created_at"` + CreatedBy string `json:"created_by,omitempty"` // Who created this issue (GH#748) UpdatedAt time.Time `json:"updated_at"` ClosedAt *time.Time `json:"closed_at,omitempty"` CloseReason string `json:"close_reason,omitempty"` // Reason provided when closing the issue @@ -97,7 +98,9 @@ func (i *Issue) ComputeContentHash() string { h.Write([]byte{0}) h.Write([]byte(i.Assignee)) h.Write([]byte{0}) - + h.Write([]byte(i.CreatedBy)) + h.Write([]byte{0}) + if i.ExternalRef != nil { h.Write([]byte(*i.ExternalRef)) }