From c28defb7107d3eb860add5fda24ff80853427f5f Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Wed, 24 Dec 2025 14:39:04 -0800 Subject: [PATCH] fix(sqlite): handle dots in prefix for extractParentChain (GH#664) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extractParentChain was using strings.Split(id, ".") which incorrectly parsed prefixes containing dots (like "alicealexandra.com"). This caused --parent to fail with "parent does not exist" even when the parent was present in the database. The fix uses IsHierarchicalID to walk up the hierarchy correctly, only splitting on dots followed by numeric suffixes (the actual hierarchy delimiter). Example: - "test.example-abc.1" now correctly returns ["test.example-abc"] - Previously it incorrectly returned ["test", "test.example-abc"] 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/storage/sqlite/resurrection.go | 27 ++++++++++++-------- internal/storage/sqlite/resurrection_test.go | 21 +++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/internal/storage/sqlite/resurrection.go b/internal/storage/sqlite/resurrection.go index 39b0cf21..1a4a2f11 100644 --- a/internal/storage/sqlite/resurrection.go +++ b/internal/storage/sqlite/resurrection.go @@ -224,17 +224,24 @@ func (s *SQLiteStorage) tryResurrectParentChainWithConn(ctx context.Context, con // extractParentChain returns all parent IDs in a hierarchical chain, ordered from root to leaf. // Example: "bd-abc.1.2" → ["bd-abc", "bd-abc.1"] +// Example: "test.example-abc.1" → ["test.example-abc"] (prefix with dot is preserved) +// +// This function uses IsHierarchicalID to correctly handle prefixes containing dots (GH#664). +// It only splits on dots followed by numeric suffixes (the hierarchy delimiter). func extractParentChain(id string) []string { - parts := strings.Split(id, ".") - if len(parts) <= 1 { - return nil // No parents (top-level ID) + var parents []string + current := id + + // Walk up the hierarchy by repeatedly finding the parent + for { + isHierarchical, parentID := IsHierarchicalID(current) + if !isHierarchical { + break // No more parents + } + // Prepend to build root-to-leaf order + parents = append([]string{parentID}, parents...) + current = parentID } - - parents := make([]string, 0, len(parts)-1) - for i := 1; i < len(parts); i++ { - parent := strings.Join(parts[:i], ".") - parents = append(parents, parent) - } - + return parents } diff --git a/internal/storage/sqlite/resurrection_test.go b/internal/storage/sqlite/resurrection_test.go index 49423420..aa7b5dcd 100644 --- a/internal/storage/sqlite/resurrection_test.go +++ b/internal/storage/sqlite/resurrection_test.go @@ -446,6 +446,27 @@ func TestExtractParentChain(t *testing.T) { id: "test-abc.1.2.3", expected: []string{"test-abc", "test-abc.1", "test-abc.1.2"}, }, + // GH#664: Prefixes with dots should be handled correctly + { + name: "prefix with dot - top-level", + id: "test.example-abc", + expected: nil, // No numeric suffix, not hierarchical + }, + { + name: "prefix with dot - one level deep", + id: "test.example-abc.1", + expected: []string{"test.example-abc"}, + }, + { + name: "prefix with dot - two levels deep", + id: "test.example-abc.1.2", + expected: []string{"test.example-abc", "test.example-abc.1"}, + }, + { + name: "prefix with multiple dots - one level deep", + id: "my.company.project-xyz.1", + expected: []string{"my.company.project-xyz"}, + }, } for _, tt := range tests {