Commit Graph

65 Commits

Author SHA1 Message Date
emma
62e4eaf7c1 fix(sync): make snapshot protection timestamp-aware (GH#865)
The --protect-left-snapshot mechanism was protecting ALL local issues
by ID alone, ignoring timestamps. This caused newer remote changes to
be incorrectly skipped during cross-worktree sync.

Changes:
- Add BuildIDToTimestampMap() to SnapshotManager for timestamp-aware
  snapshot reading
- Change ProtectLocalExportIDs from map[string]bool to map[string]time.Time
- Add shouldProtectFromUpdate() helper that compares timestamps
- Only protect if local snapshot is newer than incoming; allow update
  if incoming is newer

This fixes data loss scenarios where:
1. Main worktree closes issue at 11:31
2. Test worktree syncs and incorrectly skips the update
3. Test worktree then pushes stale open state, overwriting mains changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 13:27:36 -08:00
Steve Yegge
f46cc2e798 chore: remove issue ID references from comments and changelogs
Strip (bd-xxx), (gt-xxx) suffixes from code comments and changelog
entries. The descriptions remain meaningful without the ephemeral
issue IDs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 10:05:16 -08:00
Steve Yegge
25402184a6 fix: respect allowed_prefixes config during import (gt-2z6s)
The buildAllowedPrefixSet function now parses the allowed_prefixes config
and includes those prefixes when validating imports. This allows mol-*
prefixes to be imported without errors when configured.

Config example: bd config set allowed_prefixes "gt-,mol-"
2025-12-24 14:28:23 -08:00
Steve Yegge
9743b45c6d fix(import): preserve comment created_at timestamps during import (#735)
Comment timestamps were being overwritten with CURRENT_TIMESTAMP during
import, causing infinite sync loops between hosts as each import would
update timestamps.

Added ImportIssueComment() method that accepts and preserves the original
timestamp from JSONL, and updated importComments() to use it.

Closes #735
2025-12-24 12:45:20 -08:00
Steve Yegge
e67f27c092 bd sync: 2025-12-23 23:38:57 2025-12-24 00:06:41 -08:00
Steve Yegge
2de1695615 bd sync: 2025-12-23 22:33:32 2025-12-23 22:33:33 -08:00
Steve Yegge
cf9e5a597b bd sync: 2025-12-23 20:50:50 2025-12-23 20:50:50 -08:00
Steve Yegge
9c8761abc9 bd sync: 2025-12-23 20:45:19 2025-12-23 20:45:19 -08:00
Steve Yegge
3d924f88f3 fix: handle multi-hyphen prefixes correctly (GH#422)
The prefix mismatch detection was using ExtractIssuePrefix() which
tries to guess the prefix by analyzing suffix patterns. This failed
for multi-hyphen prefixes like "asianops-audit-" when issue IDs had
word-like suffixes (e.g., "asianops-audit-test") - the heuristic
would fall back to the first hyphen and report "asianops-" as the
prefix.

Fixed by checking directly if the issue ID starts with the configured
prefix using strings.HasPrefix(). This is more reliable than guessing
since we know the expected prefix from the database config.

Added test case TestImportMultiHyphenPrefix to prevent regression.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 16:20:04 -08:00
Steve Yegge
0d31e4209d fix: preserve pinned field during JSONL import (bd-phtv)
Fixed two code paths where pinned=false from JSONL would overwrite
existing pinned=true in database:
- importer.go: Only update pinned if explicitly true in JSONL
- multirepo.go: Use COALESCE to preserve existing pinned value

Added tests for pinned field preservation.

Note: Bug may have additional code path - investigation ongoing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 13:49:22 -08:00
Gero Hillebrandt
9d30e80fdf fix: skip prefix validation in multi-repo mode (GH#686) (#688)
Issues from additional repos have their own prefixes, which is expected.
Skip validation when multi-repo config is present.
2025-12-22 01:25:19 -08:00
Steve Yegge
5d2daf2e1e fix(pin): add 'pinned' field to allowed update fields (gt-zr0a)
The 'bd pin' command was failing with "invalid field for update: pinned"
because the pinned field was missing from allowedUpdateFields.

Fixes:
- Add 'pinned' to allowedUpdateFields in queries.go
- Update importer to include pinned field in updates during import
- Add equalBool comparator for IssueDataChanged to detect pinned changes
- Fix stats query to count pinned=1 instead of status='pinned'
- Fix pinIndicator in list.go to check issue.Pinned instead of status

This unblocks gt mail --pinned functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 19:57:16 -08:00
Justin Williams
df5ceb5d82 feat: Linear Integration (#655)
* Add Linear integration CLI with sync and status commands

- Add `bd linear sync` for bidirectional issue sync with Linear
- Add `bd linear status` to show configuration and sync state
- Stub pull/push functions pending GraphQL client (bd-b6b.2)

* Implement Linear GraphQL client with full sync support

- Add LinearClient with auth, fetch, create, update methods
- Implement pull/push operations with Beads type mapping
- Clean up redundant comments and remove unused code

* Add configurable data mapping and dependency sync for Linear

- Add LinearMappingConfig with configurable priority/state/label/relation maps
- Import parent-child and issue relations as Beads dependencies
- Support custom workflow states via linear.state_map.* config

* Add incremental sync support for Linear integration

- Add FetchIssuesSince() method using updatedAt filter in GraphQL
- Check linear.last_sync config to enable incremental pulls
- Track sync mode (incremental vs full) in LinearPullStats

* feat(linear): implement push updates for existing Linear issues

Add FetchIssueByIdentifier method to retrieve single issues by identifier
(e.g., "TEAM-123") for timestamp comparison during push.

Update doPushToLinear to:
- Fetch Linear issue to get internal ID and UpdatedAt timestamp
- Compare timestamps: only update if local is newer
- Build update payload with title, description, priority, and state
- Call UpdateIssue for issues where local has newer changes

Closes bd-b6b.5

* Implement Linear conflict resolution strategies

- Add true conflict detection by fetching Linear timestamps via API
- Implement --prefer-linear resolution (re-import from Linear)
- Implement timestamp-based resolution (newer wins as default)
- Fix linter issues: handle resp.Body.Close() and remove unused error return

* Add Linear integration tests and documentation

- Add comprehensive unit tests for Linear mapping (priority, state, labels, relations)
- Update docs/CONFIG.md with Linear configuration reference
- Add examples/linear-workflow guide for bidirectional sync
- Remove AI section header comments from tests

* Fix Linear GraphQL filter construction and improve test coverage

- Refactor filter handling to combine team ID into main filter object
- Add test for duplicate issue relation mapping
- Add HTTP round-trip helper for testing request payload validation

* Refactor Linear queries to use shared constant and add UUID validation

- Extract linearIssuesQuery to deduplicate FetchIssues/FetchIssuesSince
- Add linearMaxPageSize constant and UUID validation with regex
- Expand test coverage for new functionality

* Refactor Linear integration into internal/linear package

- Extract types, client, and mapping logic from cmd/bd/linear.go
- Create internal/linear/ package for better code organization
- Update tests to work with new package structure

* Add linear teams command to list available teams

- Add FetchTeams GraphQL query to Linear client
- Refactor config reading to support daemon mode
- Add tests for teams listing functionality

* Refactor Linear config to use getLinearConfig helper

- Consolidate config/env var lookup using getLinearConfig function
- Add LINEAR_TEAM_ID environment variable support
- Update error messages to include env var configuration options

* Add hash ID generation and improve Linear conflict detection

- Add configurable hash ID mode for Linear imports (matches bd/Jira)
- Improve conflict detection with content hash comparison
- Enhance conflict resolution with skip/force update tracking

* Fix test for updated doPushToLinear signature

- Add missing skipUpdateIDs parameter to test call
2025-12-19 17:58:24 -08:00
Steve Yegge
8b0cd1ec66 refactor(import): remove redundant pre-batch deduplication layer
The early ID deduplication in the main loop (seenIDs check) already
prevents duplicate IDs from reaching newIssues. The pre-batch
deduplication was dead code that could never fire.

Keeping two layers:
1. Early dedup in main loop - catches duplicates in JSONL input
2. INSERT OR IGNORE at DB level - safety net for all code paths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:09:26 -08:00
Marco Del Pin
dba9bb91c3 fix(import): handle duplicate issue IDs in JSONL files gracefully
Implements three-layer deduplication strategy to prevent UNIQUE
constraint errors during import:
1. Early deduplication during processing (importer.go)
2. Pre-batch deduplication (importer.go)
3. INSERT OR IGNORE with explicit error handling (issues.go)
**Problem:**
JSONL files with duplicate issue IDs caused import failures:
  Import failed: UNIQUE constraint failed: issues.id
**Root Cause:**
- Go SQLite driver returns errors even with INSERT OR IGNORE
- Only content hash was deduplicated, not IDs
- Multiple code paths affected (insertIssue, insertIssues)
**Solution:**
Layer 1: Early deduplication by ID in upsertIssues (lines 489-502)
Layer 2: Pre-batch deduplication (lines 713-726)
Layer 3: INSERT OR IGNORE + isUniqueConstraintError() helper
**Testing:**
- Multiple production databases tested
- 9 duplicates handled successfully
- 100% success rate on v0.30.5 databases
- Zero UNIQUE constraint errors
**Impact:**
- Enables importing JSONL with duplicate IDs
- Duplicate count shown in import statistics
- No breaking changes, backward compatible
🤖 Generated with Claude Code
2025-12-18 19:26:29 +01:00
Steve Yegge
0ce039429d fix: tombstone/deletion overhaul for bd-4q8
- IsExpired(): Negative TTL means immediately expired (for --hard mode)
- IsExpired(): ClockSkewGrace only added for TTLs > 1 hour
- bd cleanup --hard: Use negative TTL to prune freshly created tombstones
- bd delete --hard: New flag to immediately prune tombstones from JSONL
- Import: Add early tombstone check before all phases to prevent resurrection

The early tombstone check prevents ghost issues from being created when
tombstones exist in the DB. However, a deeper git merge issue (bd-ncwo)
can still cause resurrection when remote's status:closed wins the merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:01:31 -08:00
Steve Yegge
9f76cfda01 refactor: remove all deletions.jsonl code (bd-fom)
Complete removal of the legacy deletions.jsonl manifest system.
Tombstones are now the sole deletion mechanism.

Removed:
- internal/deletions/ - entire package
- cmd/bd/deleted.go - deleted command
- cmd/bd/doctor/fix/deletions.go - HydrateDeletionsManifest
- Tests for all removed functionality

Cleaned:
- cmd/bd/sync.go - removed sanitize, auto-compact
- cmd/bd/delete.go - removed dual-writes
- cmd/bd/doctor.go - removed checkDeletionsManifest
- internal/importer/importer.go - removed deletions checks
- internal/syncbranch/worktree.go - removed deletions merge
- cmd/bd/integrity.go - updated validation (warn-only on decrease)

Files removed: 12
Lines removed: ~7500

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 14:20:49 -08:00
Steve Yegge
2c86404d65 fix: resolve P2 sync noise and cleanup issues
- bd-6pni: Auto-filter tombstoned issues with mismatched prefixes during
  import instead of failing. Tombstones from contributor PRs with different
  test prefixes are pollution and safe to ignore.

- bd-ffr9: Stop recreating deletions.jsonl after tombstone migration.
  Added IsTombstoneMigrationComplete() check to all code paths that write
  to the legacy deletions manifest.

- bd-admx: Fix perpetual "JSONL file hash mismatch" warning. Now clears
  both export_hashes AND jsonl_file_hash when mismatch detected, so the
  warning doesn't repeat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:56:06 -08:00
Steve Yegge
8c45069228 fix(doctor,sync): clean up deletions manifest and reduce sync noise
- bd-8v5o: When doctor --fix hydrates issues from git history, also
  remove them from the deletions manifest to prevent perpetual skip
  warnings during sync

- bd-wsqt: Remove verbose per-issue "Skipping bd-xxx" messages during
  sync. Caller already shows summary of skipped issues.

Added RemoveDeletions() function to deletions package with tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:42:36 -08:00
Ryan Snodgrass
627ac1afb8 fix(import): skip cross-prefix content matches instead of triggering rename
When importing issues, if an incoming issue has the same content hash as an
existing issue but a DIFFERENT prefix, this should not be treated as a rename.
Cross-prefix content matches occur when importing issues from other projects
that happen to have identical content.

Previously, the importer would call handleRename which tries to create an issue
with the incoming prefix, failing prefix validation ("does not match configured
prefix" error).

The fix checks if prefixes differ before calling handleRename:
- Same prefix, different ID suffix → true rename, call handleRename
- Different prefix → skip incoming issue, keep existing unchanged

Added test: TestImportCrossPrefixContentMatch reproduces the bug scenario
where alpha-* issues exist but beta-* issues are imported with same content.
2025-12-16 00:29:19 -08:00
matt wilkie
cd3b0e30be bd-ckej: fix orphan skip count mismatch on fresh import (#572)
* bd-ckej: fix orphan skip count mismatch on fresh import

When OrphanSkip mode is used during import and a child issue's parent doesn't
exist, the issue ID was cleared to '' but then regenerated anyway in
GenerateBatchIssueIDs, causing it to be created in the database. This resulted
in a count mismatch: JSONL had 824 issues but only 823 were in the database (one
orphan was counted but not created).

Fix: Filter out orphaned issues with empty IDs before batch creation and track
them in result.Skipped so the count stays accurate.

* test: add TestImportOrphanSkip_CountMismatch for bd-ckej

Adds comprehensive test that verifies orphaned issues are properly skipped
during import when orphan_handling=OrphanSkip and parent doesn't exist.

Also improves the fix to pre-filter orphaned issues before batch creation,
ensuring they're not inserted then have IDs cleared (preventing count
mismatches).

---------

Co-authored-by: Amp <amp@example.com>
2025-12-15 21:17:32 -08:00
Steve Yegge
8e58c306a7 fix(sync,blocked): add safety guards for issue deletion and include status=blocked in bd blocked
GH#464: Add safety guards to prevent deletion of open/in_progress issues during sync:
- Safety guard in git-history-backfill (importer.go)
- Safety guard in deletions manifest processing
- Warning when uncommitted changes detected before pull (daemon_sync.go)
- Enhanced repo ID mismatch error message

GH#545: Fix bd blocked to show status=blocked issues (sqlite/ready.go):
- Changed from INNER JOIN to LEFT JOIN to include issues without dependencies
- Added WHERE clause to include both status=blocked AND dependency-blocked issues

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 23:07:54 -08:00
Steve Yegge
a612c575f9 fix(sync): include tombstones when building ID map during import
The importer was not seeing tombstones when building the dbByID map,
causing it to treat tombstone IDs as "new" issues. This led to UNIQUE
constraint violations during INSERT.

- Include tombstones in SearchIssues call (IncludeTombstones: true)
- Skip tombstones when matching by ID instead of trying to update them

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 10:20:22 -08:00
Rod Davenport
6985ea94e5 fix(sync): protect local issues from git-history-backfill during sync (#485)
Fix sync bug where newly created issues were incorrectly tombstoned during bd sync.

The root cause was git-history-backfill finding issues in local commits on the sync branch, then tombstoning them when they weren't in the merged JSONL. The fix protects issues from the left snapshot (local export) from git-history-backfill.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-12-12 13:28:48 -08:00
Steve Yegge
24917f27c2 fix(importer): use zero for unknown priority in convertDeletionToTombstone (bd-9auw)
Changed Priority from hardcoded 2 to 0 (unset) to distinguish legacy tombstones
from user-set values. IssueType remains TypeTask as empty fails validation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 21:20:00 +11:00
Steve Yegge
711dd9ccd4 feat(importer): add ConvertedToTombstone counter (bd-wucl)
Track legacy deletions.jsonl entries converted to tombstones during import:
- Add Result.ConvertedToTombstone counter
- Add Result.ConvertedTombstoneIDs for the converted IDs
- Update test to verify the new counter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 21:18:17 +11:00
Steve Yegge
f4864c9cc4 feat(import/export): add tombstone support (bd-dve)
Update import/export to handle tombstones for deletion sync propagation:

Exporter:
- Include tombstones in JSONL output by setting IncludeTombstones: true
- Both single-repo and multi-repo exports now include tombstones

Importer:
- Tombstones from JSONL are imported as-is (they're issues with status=tombstone)
- Legacy deletions.jsonl entries are converted to tombstones via convertDeletionToTombstone()
- Non-tombstone issues in deletions manifest are still skipped (backward compat)
- purgeDeletedIssues() now creates tombstones instead of hard-deleting

This is Phase 2 of the tombstone implementation (bd-dli design), enabling
inline soft-delete tracking for cross-clone deletion synchronization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 20:34:02 +11:00
Steve Yegge
8d19e75431 fix: add safety guard to prevent git-history-backfill mass deletion (bd-t5m)
When a clone gets reset (git reset --hard origin/main), the
git-history-backfill logic was incorrectly marking ALL issues as
deleted since they appeared in git history but not in the current
JSONL. This caused the entire database to be purged.

Fix:
- Add 50% threshold check: abort if git-history-backfill would delete
  more than 50% of issues (likely a reset scenario, not deletions)
- Add warning when >10 issues would be deleted via backfill
- Print helpful message about manual deletion if needed

Test:
- Added TestMassDeletionSafetyGuard that simulates JSONL reset and
  verifies the safety guard prevents mass deletion

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 21:26:16 -08:00
Steve Yegge
f7dea354d8 fix(import): add warning when issues are skipped due to deletions manifest
When importing JSONL that contains issues in the deletions manifest,
import now:
- Filters out deleted issues before import
- Prints per-issue warning with deletion details (date, actor)
- Shows count of skipped issues in summary
- Suggests --ignore-deletions flag to force import

The new --ignore-deletions flag allows importing issues that are in the
deletions manifest, useful for recovering accidentally deleted issues.

Fixes bd-4zy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 21:23:04 -08:00
Steve Yegge
22d34a22dc Fix bd migrate loop: skip prefix validation during auto-import
When auto-importing issues from JSONL, issues with different prefixes
(e.g., gt-1 vs gastown-) would fail validation and cause an infinite
loop of failed migrations.

The fix adds SkipPrefixValidation option to CreateIssuesWithFullOptions
which propagates through EnsureIDs to skip prefix validation for issues
that already have IDs during import. This allows importing issues with
any prefix while still validating new issues created interactively.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 21:45:42 -08:00
Steve Yegge
5506486dc5 feat(import,sync): add --no-git-history flag to prevent spurious deletions
Fixes bd-0b2: The git history backfill mechanism was causing data loss
during JSONL filename migrations (beads.jsonl → issues.jsonl). When issues
existed in the old filename's git history, the backfill incorrectly treated
them as "deleted" and purged them from the database.

Changes:
- Add NoGitHistory field to importer.Options and ImportOptions structs
- Modify purgeDeletedIssues() to skip git history check when flag is set
- Add --no-git-history flag to bd import command
- Add --no-git-history flag to bd sync command
- Update purge_test.go to pass Options argument

Usage:
  bd import -i .beads/issues.jsonl --no-git-history
  bd sync --no-git-history

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 23:10:43 -08:00
Steve Yegge
ff3352ab23 bd-6xd: Standardize on issues.jsonl as canonical filename
- Change default JSONL filename from beads.jsonl to issues.jsonl
- Add bd doctor check and fix to auto-migrate legacy beads.jsonl configs
- Update FindJSONLPath to prefer issues.jsonl over beads.jsonl
- Add CheckLegacyJSONLConfig and CheckLegacyJSONLFilename checks
- Add LegacyJSONLConfig fix to rename files and update config
- Update .gitattributes to reference issues.jsonl
- Fix tests to expect new canonical filename
- Add bd-6xd to v0.25.1 release notes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 22:02:59 -08:00
Steve Yegge
4088e68da7 feat(deletions): complete deletions manifest epic with integration tests
Completes the deletion propagation epic (bd-imj) with all 9 subtasks:
- Cross-clone deletion propagation via deletions.jsonl
- bd deleted command for audit trail
- Auto-compact during sync (opt-in)
- Git history fallback with timeout and regex escaping
- JSON output for pruning results
- Integration tests for deletion scenarios
- Documentation in AGENTS.md, README.md, and docs/DELETIONS.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 16:36:46 -08:00
Steve Yegge
4898c424aa feat(deletions): auto-compact during sync and git history fallback fixes
- Add Count function to deletions package for fast line counting
- Add maybeAutoCompactDeletions to sync (opt-in via deletions.auto_compact config)
- Fix regex escaping in batchCheckGitHistory (bd-bgs)
- Add 30s timeout to git history commands (bd-f0n)
- Use git rev-parse --show-toplevel for proper repo root detection (bd-bhd)
- Add tests for Count and auto-compact functionality

Closes: bd-qsm, bd-bgs, bd-f0n, bd-bhd
2025-11-25 15:08:12 -08:00
Steve Yegge
3f84ec3774 feat(deletions): add pruning and git history fallback
Implements two P1 tasks for the deletions manifest epic:

bd-v2x: Add deletions pruning to bd compact
- PruneDeletions function removes records older than retention period
- Default retention: 7 days (configurable via metadata.json)
- CLI --retention flag for override
- Atomic file rewrite prevents corruption
- Called automatically during all compact operations

bd-pnm: Add git history fallback for pruned deletions
- Catches deletions where manifest entry was pruned
- Uses git log -S to search for ID in JSONL history
- Batches multiple IDs for efficiency (git -G regex)
- Self-healing: backfills manifest on hit
- Conservative: keeps issue if git check fails (shallow clone)

Tests added for both features with edge cases covered.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 12:41:36 -08:00
Steve Yegge
be784a0b4b bd sync: 2025-11-25 11:46:06 2025-11-25 11:46:06 -08:00
Steve Yegge
d45cff5085 feat: Handle FOREIGN KEY constraint violations gracefully during import (bd-koab)
When importing JSONL after merges that include deletions, FK constraint
violations can occur if an issue references a deleted issue. Previously,
import would fail completely. Now it continues and reports skipped dependencies.

Changes:
- Add SkippedDependencies field to Result/ImportResult structs
- Update importDependencies() to detect FK violations using IsForeignKeyConstraintError()
- Log warnings for each skipped dependency with issue IDs and type
- Continue importing remaining dependencies instead of failing
- Display summary of all skipped dependencies at end of import

Example output:
  Warning: Skipping dependency due to missing reference: bd-b → bd-a (blocks)

  ⚠️  Warning: Skipped 2 dependencies due to missing references:
    - bd-b → bd-a (blocks)
    - bd-c → bd-a (parent-child)

  This can happen after merges that delete issues referenced by other issues.
  The import continued successfully - you may want to review the skipped dependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 23:32:34 -08:00
Steve Yegge
57253f93a3 Context propagation with graceful cancellation (bd-rtp, bd-yb8, bd-2o2)
Complete implementation of signal-aware context propagation for graceful
cancellation across all commands and storage operations.

Key changes:

1. Signal-aware contexts (bd-rtp):
   - Added rootCtx/rootCancel in main.go using signal.NotifyContext()
   - Set up in PersistentPreRun, cancelled in PersistentPostRun
   - Daemon uses same pattern in runDaemonLoop()
   - Handles SIGINT/SIGTERM for graceful shutdown

2. Context propagation (bd-yb8):
   - All commands now use rootCtx instead of context.Background()
   - sqlite.New() receives context for cancellable operations
   - Database operations respect context cancellation
   - Storage layer propagates context through all queries

3. Cancellation tests (bd-2o2):
   - Added import_cancellation_test.go with comprehensive tests
   - Added export cancellation test in export_test.go
   - Tests verify database integrity after cancellation
   - All cancellation tests passing

Fixes applied during review:
   - Fixed rootCtx lifecycle (removed premature defer from PersistentPreRun)
   - Fixed test context contamination (reset rootCtx in test cleanup)
   - Fixed export tests missing context setup

Impact:
   - Pressing Ctrl+C during import/export now cancels gracefully
   - No database corruption or hanging transactions
   - Clean shutdown of all operations

Tested:
   - go build ./cmd/bd ✓
   - go test ./cmd/bd -run TestImportCancellation ✓
   - go test ./cmd/bd -run TestExportCommand ✓
   - Manual Ctrl+C testing verified

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 21:57:23 -05:00
Steve Yegge
9de98cf1cb Add --clear-duplicate-external-refs flag to bd import
Fixes GH-234 by providing automatic resolution for duplicate external_ref
values instead of forcing manual JSONL editing.

Changes:
- Add ClearDuplicateExternalRefs option to importer.Options
- Modify validateNoDuplicateExternalRefs to clear duplicates when enabled
- Keep first occurrence, clear rest when flag is set
- Enhanced error message to suggest the flag
- Add comprehensive tests for the new behavior

Usage: bd import -i issues.jsonl --clear-duplicate-external-refs
Amp-Thread-ID: https://ampcode.com/threads/T-932dcf45-76f2-4994-9b5c-a6eb20a86036
Co-authored-by: Amp <amp@ampcode.com>
2025-11-06 13:01:44 -08:00
Steve Yegge
0abd21f7a6 Fix bd-gdzd: Treat same-content-different-ID as update
When import finds same content hash with different IDs, treat it as
an update to the existing issue instead of failing with 'rename
collision' error. This handles edge cases like test data, legacy
data, or data corruption gracefully.

Amp-Thread-ID: https://ampcode.com/threads/T-e58a11be-cbbb-4a75-86d5-fc51af8f51d2
Co-authored-by: Amp <amp@ampcode.com>
2025-11-05 00:54:25 -08:00
Steve Yegge
8b9a486056 Fix critical import bug: preserve closed_at timestamps during sync
**Problem:**
Closed issues were silently reopening during git sync/import operations.
When importing an issue update, the importer built an updates map with
status='closed' but NO closed_at timestamp. The UpdateIssue() function's
manageClosedAt() would only set closed_at when status was CHANGING to
closed, not when it was already closed. Result: closed_at got cleared,
effectively reopening issues.

**Root Cause:**
1. Importer built updates map without closed_at field (lines 443-451, 519-528)
2. closed_at was not in allowedUpdateFields whitelist
3. manageClosedAt() only managed closed_at for status TRANSITIONS
4. Import of already-closed issue → closed_at lost → issue reopens

**Impact:**
- WASM issues (bd-44d0, bd-8507, etc.) were closed on Nov 4 (commit 0df9144)
- They reopened as 'open' status during sync on Nov 5 (commit 8c9814a)
- Users had to repeatedly close the same issues
- Data integrity violation: status=closed with closed_at=NULL

**Fix:**
1. Add closed_at to allowedUpdateFields whitelist
2. Add closed_at to importer updates maps (both external_ref and ID paths)
3. Update manageClosedAt() to skip auto-management if closed_at explicitly provided
   - Preserves import timestamps while maintaining auto-management for CLI operations

**Testing:**
- All internal/importer tests pass
- All internal/storage/sqlite tests pass
- Explicitly tests timestamp preservation in TestImportWithExternalRef

**Files Changed:**
- internal/importer/importer.go: Add closed_at to updates maps
- internal/storage/sqlite/sqlite.go: Allow closed_at updates, respect explicit values

Amp-Thread-ID: https://ampcode.com/threads/T-53ed6e45-9d04-4a35-97e9-d1ec36321ab0
Co-authored-by: Amp <amp@ampcode.com>
2025-11-05 00:41:10 -08:00
Steve Yegge
ff8f6ecadf feat(import): add import.orphan_handling config with 4 modes
- Add GetOrphanHandling() helper to SQLiteStorage (reads from config table)
- Add --orphan-handling flag to 'bd import' command
- Wire OrphanHandling through ImportOptions -> importer.Options
- Auto-read config if flag not provided (default: 'allow')
- Document in CONFIG.md with detailed mode explanations

Modes:
- strict: Fail on missing parent (safest)
- resurrect: Auto-create parent tombstones from JSONL
- skip: Skip orphans with warning
- allow: Import without validation (default, most permissive)

Closes bd-8072, bd-b92a

Amp-Thread-ID: https://ampcode.com/threads/T-fd18d4a5-06b3-4400-9073-194d570846d8
Co-authored-by: Amp <amp@ampcode.com>
2025-11-04 23:59:50 -08:00
Steve Yegge
0bf5c91cb3 Wire OrphanHandling through import pipeline (bd-8072)
- Added OrphanHandling type to sqlite package with 4 modes: strict/resurrect/skip/allow
- Updated EnsureIDs() to accept orphanHandling parameter and implement mode logic
- Added CreateIssuesWithOptions() that passes orphan handling through batch creation
- Made importer.OrphanHandling an alias to sqlite.OrphanHandling
- Importer now respects opts.OrphanHandling during batch issue creation

Next: Add import.orphan_handling config and wire through CLI commands
Amp-Thread-ID: https://ampcode.com/threads/T-bb7ffdd9-f444-4975-b5f7-bfff97cb92ff
Co-authored-by: Amp <amp@ampcode.com>
2025-11-04 23:53:44 -08:00
Steve Yegge
09f2edafba Add OrphanHandling type with 'allow' as default (bd-8072)
- Add OrphanHandling enum: strict/resurrect/skip/allow
- Add OrphanHandling field to importer.Options
- Default to 'allow' mode to work around existing system bugs
- Strict mode can be enabled via config for safer imports

Related: bd-8072, bd-b92a
2025-11-04 23:40:22 -08:00
Steve Yegge
d38a312583 Fix bd-3xq: Import gracefully handles missing parents
Implemented hybrid approach (topological sort + resurrection):

Phase 1: Import ordering (fixes latent bug)
- Sort issues by hierarchy depth before batch creation
- Create in depth-ordered batches (0→1→2→3)
- Ensures parents always created before children

Phase 2: Parent resurrection
- Attempt to resurrect missing parents from import batch
- Only fail if parent truly doesn't exist anywhere
- Enables deleted parent scenarios to work correctly

Benefits:
- Fixes import failure when parents deleted via bd-delete
- Handles parent-child pairs in same import batch
- Maintains referential integrity
- Enables multi-repo workflows with divergent deletion states

Amp-Thread-ID: https://ampcode.com/threads/T-14d3a206-aeac-4499-8ae9-47f3715e18fa
Co-authored-by: Amp <amp@ampcode.com>
2025-11-04 23:12:39 -08:00
Steve Yegge
d6e2ff6151 Phase 1: Add topological sorting to fix import ordering
- Add sort.go with depth-based utilities (GetHierarchyDepth, SortByDepth, GroupByDepth)
- Sort issues by hierarchy depth before batch creation
- Create in depth-order batches (0→1→2→3)
- Fixes latent bug: parent-child pairs in same batch could fail if wrong order
- Comprehensive tests for all sorting functions
- Closes bd-37dd, bd-3433, bd-8b65

Part of bd-d19a (Fix import failure on missing parent issues)

Amp-Thread-ID: https://ampcode.com/threads/T-44a36985-b59c-426f-834c-60a0faa0f9fb
Co-authored-by: Amp <amp@ampcode.com>
2025-11-04 22:25:26 -08:00
Steve Yegge
178a43dd5d Add external_ref UNIQUE constraint and validation
- Add migration for UNIQUE index on external_ref column (bd-897a)
- Add validation for duplicate external_ref in batch imports (bd-7315)
- Add query planner test to verify index usage (bd-f9a1)
- Add concurrent import tests for external_ref (bd-3f6a)

The migration detects existing duplicates and fails gracefully.
Batch imports now reject duplicates with clear error messages.
Tests verify the index is actually used by SQLite query planner.

Amp-Thread-ID: https://ampcode.com/threads/T-45ca66ed-3912-46c4-963c-caa7724a9a2f
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 16:27:42 -08:00
Steve Yegge
55c722a3e3 Implement external_ref as primary matching key for import updates (bd-1022)
- Add GetIssueByExternalRef() query function to storage interface and implementations
- Update DetectCollisions() to prioritize external_ref matching over ID matching
- Modify upsertIssues() to handle external_ref matches in import logic
- Add index on external_ref column for performance
- Add comprehensive tests for external_ref matching in both collision detection and import
- Enables re-syncing from external systems (Jira, GitHub, Linear) without duplicates
- Preserves local issues (no external_ref) from being overwritten
2025-11-02 15:28:09 -08:00
Steve Yegge
09bd4d3462 Fix CI test failures (bd-1231)
- Always recompute content_hash in importer to avoid stale hashes from JSONL
- Add .gitignore to test repos to prevent database files from being tracked
- Fix daemon auto-import test to use correct RPC operation ('show' not 'get_issue')
- Set last_import_time metadata in test helper to enable staleness check
- Add filesystem settle delay after git pull in tests

Root cause: CloseIssue updates status but not content_hash, so importer
thought issues were unchanged. Always recomputing content_hash fixes this.

Amp-Thread-ID: https://ampcode.com/threads/T-63ef3a7d-8efe-472d-97ed-6ac95bd8318b
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 08:55:34 -08:00
Steve Yegge
d240439868 fix: Resolve Windows test failures and lint warnings
- Fix Windows binary path issues (bd.exe vs bd)
- Skip scripttest on Windows (requires Unix shell)
- Skip file lock tests on Windows (platform locking differences)
- Fix registry tests to use USERPROFILE on Windows
- Fix 8 unparam lint warnings by marking unused params with _

All changes are platform-aware and maintain functionality.

Amp-Thread-ID: https://ampcode.com/threads/T-bc27021a-65db-4b64-a3f3-4e8d7bc8aa0d
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 08:30:31 -08:00