Implement bd-10: Export/import dependencies in JSONL
Added dependency support to export/import workflow:
Changes:
- Added GetDependencyRecords() to Storage interface to get raw dependency records
- Extended Issue struct with Dependencies field (omitempty for backward compat)
- Modified export.go to populate dependencies for each issue
- Modified import.go to process dependencies in second pass after all issues exist
- All tests pass
Benefits:
- JSONL is now self-contained with full dependency information
- Enables proper collision resolution in future (bd-12+)
- Idempotent imports: existing dependencies are not duplicated
- Forward references handled: dependencies created after all issues exist
Example output:
{
"id": "bd-10",
"title": "...",
"dependencies": [{
"issue_id": "bd-10",
"depends_on_id": "bd-9",
"type": "parent-child",
"created_at": "...",
"created_by": "stevey"
}]
}
Closes bd-10
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,38 @@ func (s *SQLiteStorage) GetDependents(ctx context.Context, issueID string) ([]*t
|
||||
return scanIssues(rows)
|
||||
}
|
||||
|
||||
// GetDependencyRecords returns raw dependency records for an issue
|
||||
func (s *SQLiteStorage) GetDependencyRecords(ctx context.Context, issueID string) ([]*types.Dependency, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT issue_id, depends_on_id, type, created_at, created_by
|
||||
FROM dependencies
|
||||
WHERE issue_id = ?
|
||||
ORDER BY created_at ASC
|
||||
`, issueID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get dependency records: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var deps []*types.Dependency
|
||||
for rows.Next() {
|
||||
var dep types.Dependency
|
||||
err := rows.Scan(
|
||||
&dep.IssueID,
|
||||
&dep.DependsOnID,
|
||||
&dep.Type,
|
||||
&dep.CreatedAt,
|
||||
&dep.CreatedBy,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan dependency: %w", err)
|
||||
}
|
||||
deps = append(deps, &dep)
|
||||
}
|
||||
|
||||
return deps, nil
|
||||
}
|
||||
|
||||
// GetDependencyTree returns the full dependency tree
|
||||
func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*types.TreeNode, error) {
|
||||
if maxDepth <= 0 {
|
||||
|
||||
@@ -21,6 +21,7 @@ type Storage interface {
|
||||
RemoveDependency(ctx context.Context, issueID, dependsOnID string, actor string) error
|
||||
GetDependencies(ctx context.Context, issueID string) ([]*types.Issue, error)
|
||||
GetDependents(ctx context.Context, issueID string) ([]*types.Issue, error)
|
||||
GetDependencyRecords(ctx context.Context, issueID string) ([]*types.Dependency, error)
|
||||
GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*types.TreeNode, error)
|
||||
DetectCycles(ctx context.Context) ([][]*types.Issue, error)
|
||||
|
||||
|
||||
@@ -8,20 +8,21 @@ import (
|
||||
|
||||
// Issue represents a trackable work item
|
||||
type Issue struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Design string `json:"design,omitempty"`
|
||||
AcceptanceCriteria string `json:"acceptance_criteria,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
Priority int `json:"priority"`
|
||||
IssueType IssueType `json:"issue_type"`
|
||||
Assignee string `json:"assignee,omitempty"`
|
||||
EstimatedMinutes *int `json:"estimated_minutes,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ClosedAt *time.Time `json:"closed_at,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Design string `json:"design,omitempty"`
|
||||
AcceptanceCriteria string `json:"acceptance_criteria,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
Priority int `json:"priority"`
|
||||
IssueType IssueType `json:"issue_type"`
|
||||
Assignee string `json:"assignee,omitempty"`
|
||||
EstimatedMinutes *int `json:"estimated_minutes,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ClosedAt *time.Time `json:"closed_at,omitempty"`
|
||||
Dependencies []*Dependency `json:"dependencies,omitempty"` // Populated only for export/import
|
||||
}
|
||||
|
||||
// Validate checks if the issue has valid field values
|
||||
|
||||
Reference in New Issue
Block a user