Migration tool: sequential → hash IDs

Create migration tool to convert existing sequential-ID databases to hash-ID format.

## Command: bd migrate --hash-ids

```bash
bd migrate --hash-ids [--dry-run]
```

## Migration Process

### 1. Backup Database
```bash
cp .beads/beads.db .beads/beads.db.backup-1761798384
echo "✓ Backup created: .beads/beads.db.backup-1234567890"
```

### 2. Generate Hash IDs for Existing Issues
```go
func migrateToHashIDs(db *SQLiteStorage) error {
    // Read all issues
    issues, err := db.ListIssues(ctx, ListOptions{Status: "all"})

    // Generate mapping: old ID → new hash ID
    mapping := make(map[string]string)
    for _, issue := range issues {
        hashID := GenerateHashID(
            issue.Title,
            issue.Description,
            issue.CreatedAt,
            db.workspaceID,
        )
        mapping[issue.ID] = hashID
    }

    // Detect hash collisions (extremely rare)
    if hasCollisions(mapping) {
        return fmt.Errorf("hash collision detected, aborting")
    }

    return nil
}
```

### 3. Update Database Schema
```sql
-- Add alias column
ALTER TABLE issues ADD COLUMN alias INTEGER UNIQUE;

-- Populate aliases from old IDs
UPDATE issues SET alias = CAST(SUBSTR(id, 4) AS INTEGER)
  WHERE id LIKE 'bd-%' AND SUBSTR(id, 4) GLOB '[0-9]*';

-- Create new issues_new table with hash IDs
CREATE TABLE issues_new (
    id TEXT PRIMARY KEY,  -- Hash IDs now
    alias INTEGER UNIQUE,
    title TEXT NOT NULL,
    -- ... rest of schema
);

-- Copy data with ID mapping
INSERT INTO issues_new SELECT
    <new_hash_id>,  -- From mapping
    alias,
    title,
    -- ...
FROM issues;

-- Drop old table, rename new
DROP TABLE issues;
ALTER TABLE issues_new RENAME TO issues;
```

### 4. Update Dependencies
```sql
-- Update depends_on_id using mapping
UPDATE dependencies
SET issue_id = <new_hash_id>,
    depends_on_id = <new_depends_on_hash_id>
FROM mapping;
```

### 5. Update Text References
```go
// Update all text fields that mention old IDs
func updateTextReferences(db *SQLiteStorage, mapping map[string]string) error {
    for oldID, newID := range mapping {
        // Update description, notes, design, acceptance_criteria
        db.Exec(`UPDATE issues SET
            description = REPLACE(description, ?, ?),
            notes = REPLACE(notes, ?, ?),
            design = REPLACE(design, ?, ?),
            acceptance_criteria = REPLACE(acceptance_criteria, ?, ?)
        `, oldID, newID, oldID, newID, oldID, newID, oldID, newID)
    }
}
```

### 6. Export to JSONL
```bash
bd export  # Writes hash IDs to .beads/issues.jsonl
git add .beads/issues.jsonl
git commit -m "Migrate to hash-based IDs"
```

## Output
```bash
$ bd migrate --hash-ids
Migrating to hash-based IDs...
✓ Backup created: .beads/beads.db.backup-1730246400
✓ Generated 150 hash IDs
✓ No hash collisions detected
✓ Updated issues table schema
✓ Updated 150 issue IDs
✓ Updated 87 dependencies
✓ Updated 234 text references
✓ Exported to .beads/issues.jsonl
✓ Migration complete!

Next steps:
  1. Test: bd list, bd show #1, etc.
  2. Commit: git commit -m "Migrate to hash-based IDs"
  3. Push: git push origin main
  4. Notify collaborators to pull and re-init
```

## Dry Run Mode
```bash
$ bd migrate --hash-ids --dry-run
[DRY RUN] Would migrate 150 issues:
  bd-1c63eb84   → bd-af78e9a2  (alias: #1)
  bd-9063acda   → bd-e5f6a7b8  (alias: #2)
  ...
  bd-150 → bd-9a8b7c6d  (alias: #150)

[DRY RUN] Would update:
  - 150 issue IDs
  - 87 dependencies
  - 234 text references in descriptions/notes

No changes made. Run without --dry-run to apply.
```

## Files to Create
- cmd/bd/migrate.go (new command)
- internal/storage/sqlite/migrations/hash_ids.go

## Testing
- Test migration on small database (10 issues)
- Test migration on large database (1000 issues)
- Test hash collision detection (inject collision artificially)
- Test text reference updates
- Test rollback (restore from backup)
- Test migrated database works correctly

## Rollback Procedure
```bash
# If migration fails or has issues
mv .beads/beads.db.backup-1234567890 .beads/beads.db
bd export  # Restore JSONL from backup DB
```

## Multi-Clone Coordination
**Important**: All clones must migrate before syncing:

1. Coordinator sends message: "Migrating to hash IDs on 2025-10-30 at 10:00 UTC"
2. All collaborators pull latest changes
3. All run: `bd migrate --hash-ids`
4. All push changes
5. New work can continue with hash IDs

**Do NOT**:
- Mix sequential and hash IDs in same database
- Sync before all clones migrate


Migration and onboarding for multi-repo

Create migration tools, wizards, and documentation to help users adopt multi-repo workflow, with special focus on OSS contributor onboarding and team adoption scenarios.


bd cleanup fails with CHECK constraint on status/closed_at mismatch

## Problem

Running `bd cleanup --force` fails with:

```
Error: failed to create tombstone for bd-okh: sqlite3: constraint failed: CHECK constraint failed: (status = 'closed') = (closed_at IS NOT NULL)
```

## Root Cause

The database has a CHECK constraint ensuring closed issues have `closed_at` set and non-closed issues don't. When `bd cleanup` tries to convert a closed issue to a tombstone, this constraint fails if:
1. The issue has status='closed' but closed_at is NULL, OR
2. The issue is being converted but the tombstone creation doesn't properly handle the closed_at field

## Impact

- `bd cleanup --force` cannot complete
- 722 closed issues cannot be cleaned up
- Blocks routine maintenance

## Investigation Needed

1. Check the bd-okh record in the database:
   ```sql
   SELECT id, status, closed_at, deleted_at FROM issues WHERE id = 'bd-okh';
   ```
2. Determine if this is data corruption or a bug in tombstone creation

## Proposed Solutions

1. **If data corruption**: Add a `bd doctor --fix` check that repairs status/closed_at mismatches
2. **If code bug**: Fix the tombstone creation to properly handle the constraint

## Files to Investigate

- `internal/storage/sqlite/sqlite.go` - DeleteIssue / tombstone creation
- Schema CHECK constraint definition

## Acceptance Criteria

- [ ] Identify root cause (data vs code bug)
- [ ] `bd cleanup --force` completes successfully
- [ ] `bd doctor` detects status/closed_at mismatches


Event-driven daemon architecture

Replace 5-second polling sync loop with event-driven architecture that reacts instantly to changes. Eliminates stale data issues while reducing CPU ~60%. Key components: FileWatcher (fsnotify), Debouncer (500ms), RPC mutation events, optional git hooks. Target latency: <500ms (vs 5000ms). See event_driven_daemon.md for full design.

Production-ready after 3 critical fixes (commit 349b892):
- Skip redundant imports (mtime check prevents self-trigger loops)
- Add server.Stop() in serverErrChan case (clean shutdown)
- Fallback ticker (60s) when watcher unavailable (ensures remote sync)

Ready to make default after integration test (bd-1f4086c5.1) passes.


Implement signal-aware context in CLI commands

Replace context.Background() with signal.NotifyContext() to enable graceful cancellation.

## Context
Part of context propagation work (bd-350). Phase 1 infrastructure is complete - sqlite.New() now accepts context parameter.

## Implementation
Set up signal-aware context at the CLI entry points:

```go
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
```

## Key Locations
- cmd/bd/main.go:438 (marked with TODO(bd-350))
- cmd/bd/daemon.go:317 (marked with TODO(bd-350))
- Other command entry points

## Benefits
- Ctrl+C cancels ongoing database operations
- Graceful shutdown on SIGTERM
- Better user experience during long operations

## Acceptance Criteria
- [ ] Ctrl+C during import cancels operation cleanly
- [ ] Ctrl+C during export cancels operation cleanly
- [ ] No database corruption on cancellation
- [ ] Proper cleanup on signal (defers execute)


Test parent issue


Document deletions manifest in AGENTS.md and README

Parent: bd-imj

## Task
Add documentation about the deletions manifest feature.

## Locations to Update

### AGENTS.md
- Explain that deletions.jsonl is tracked in git
- Document that `bd delete` records to the manifest
- Explain cross-clone propagation mechanism

### README.md
- Brief mention in .beads directory structure section
- Link to detailed docs if needed

### docs/deletions.md (new file)
- Full technical documentation
- Format specification
- Pruning policy
- Git history fallback
- Troubleshooting

## Acceptance Criteria
- AGENTS.md updated with deletion workflow
- README.md mentions deletions.jsonl purpose
- New docs/deletions.md with complete reference


Implement "bd daemons logs" subcommand

Add command to view daemon logs for a specific workspace. Requires daemon logging to file (may need separate issue for log infrastructure).


Update AGENTS.md with event-driven mode

Document BEADS_DAEMON_MODE env var. Explain opt-in during Phase 1. Add troubleshooting for watcher failures.


Investigate database pollution - unexpected issue count increases

Two repositories showing unexpected issue counts:
- ~/src/beads: 280 issues (expected ~209-220)
- ~/src/dave/beads: 895 issues (675 open, 149 closed)

This suggests database pollution - issues from one repository leaking into another. Need to investigate:
1. Run bd detect-pollution on both repos
2. Check for cross-repo contamination
3. Identify source of pollution (daemon? multi-repo config? import issues?)
4. Clean up polluted databases
5. Prevent future pollution


Second worktree test


Implement backup functionality for reset

Add backup capability that can be used by reset command.

## Functionality
- Copy .beads/ to .beads-backup-{timestamp}/
- Timestamp format: YYYYMMDD-HHMMSS
- Preserve file permissions
- Return backup path for user feedback

## Location
`internal/reset/backup.go` - keep with reset package for now (YAGNI)

## Interface
```go
func CreateBackup(beadsDir string) (backupPath string, err error)
```

## Notes
- Simple recursive file copy, no compression needed
- Error if backup dir already exists (unlikely with timestamp)
- Backup directories SHOULD be gitignored
- Add `.beads-backup-*/` pattern to .beads/.gitignore template in doctor package
- Consider: ListBackups() for future `bd backup list` command (not for this PR)


Fix FOREIGN KEY constraint failures in AddComment and ApplyCompaction

The 'CloseIssue', 'UpdateIssueID', and 'RemoveLabel' methods were fixed in PR #348 to prevent foreign key constraint failures when operating on non-existent issues.

However, the Oracle identified two other methods that follow the same problematic pattern:
1. `AddComment` (in `internal/storage/sqlite/events.go`)
2. `ApplyCompaction` (in `internal/storage/sqlite/compact.go`)

These methods attempt to insert an event record after updating the issue, without verifying that the issue update actually affected any rows. This leads to a foreign key constraint failure if the issue does not exist.

We need to:
1. Create reproduction tests for these failure cases
2. Apply the same fix pattern: check `RowsAffected()` after the update, and return a proper "issue not found" error if it is 0, before attempting to insert the event.
3. Standardize the error message format to "issue %s not found" or "issue not found: %s" for consistency.


Investigate TestRoutingIntegration failure

TestRoutingIntegration/maintainer_with_SSH_remote failed during pre-commit check with "expected role maintainer, got contributor".
This occurred while running `go test -short ./...` on darwin/arm64.
The failure appears unrelated to storage/sqlite changes.
Need to investigate if this is a flaky test or environmental issue.


Add .beads/README.md during 'bd init' for project documentation and promotion

When 'bd init' is run, automatically generate a .beads/README.md file that:

1. Briefly explains what Beads is (AI-native issue tracking that lives in your repo)
2. Links to the main repository: https://github.com/steveyegge/beads
3. Provides a quick reference of essential commands:
   - bd create: Create new issues
   - bd list: View all issues
   - bd update: Modify issue status/details
   - bd show: View issue details
   - bd sync: Sync with git remote
4. Highlights key benefits for AI coding agents and developers
5. Encourages developers to try it out

The README should be enthusiastic and compelling to get open source contributors excited about using Beads for their AI-assisted development workflows.


Jira export script (jsonl2jira.py)

Create a Python script to push beads issues to Jira.

**Requires**: Jira import script to be complete first (need external_ref matching logic working)

**Features needed**:
- Create new Jira issues from beads issues without external_ref
- Update existing Jira issues matched by external_ref
- Map beads fields back to Jira fields
- Handle Jira workflow transitions (status changes may need transitions)
- Support custom field mapping for design/acceptance_criteria/notes

**Challenges**:
- Jira status changes often require workflow transitions, not direct updates
- Need to discover valid transitions via API
- Custom fields vary by Jira instance

**Usage**:
```bash
bd export | python jsonl2jira.py --create-only  # Only create, don't update
bd export | python jsonl2jira.py                 # Create and update
```

**After creation**: Sets external_ref on beads issue to link back


Review PR #512: clarify bd ready docs

Review and merge PR #512 from aspiers. This PR clarifies what bd ready does after git pull in README.md. Simple 1-line change. URL: https://github.com/anthropics/beads/pull/512


bd sync fails with orphaned issues and duplicate ID conflict

After fixing the deleted_at TEXT column scanning bug (commit 18b1eb2), bd sync still fails with two issues:

1. Orphan Detection Warning: 12 orphaned child issues whose parents no longer exist (bd-cb64c226.* and bd-cbed9619.*)

2. Import Failure: UNIQUE constraint failed for bd-360 - this tombstone exists in both DB and JSONL

Error: "Import failed: error creating depth-0 issues: bulk insert issues: failed to insert issue bd-360: sqlite3: constraint failed: UNIQUE constraint failed: issues.id"

Fix options:
- Delete orphaned child issues with bd delete
- Resolve bd-360 duplicate (in deletions.jsonl vs tombstone in DB)
- Reset sync branch: git branch -f beads-sync main && git push --force-with-lease origin beads-sync

Fixed tombstone constraint violation bug. When deleting closed issues, the CHECK constraint (status = 'closed') = (closed_at IS NOT NULL) was violated because CreateTombstone didn't clear closed_at. Fix: set closed_at = NULL in tombstone creation SQL.

The sync data corruption (orphaned issues in beads-sync branch) requires manual cleanup: reset sync branch with 'git branch -f beads-sync main && git push --force-with-lease origin beads-sync'


Add .beads-backup-* pattern to gitignore template

Update the gitignore template in doctor package to include backup directories.

## Change
Add `.beads-backup-*/` to the GitignoreTemplate in `cmd/bd/doctor/gitignore.go`

## Why
Backup directories created by `bd reset --backup` should not be committed to git.
They are local-only recovery tools.

## File
`cmd/bd/doctor/gitignore.go` - look for GitignoreTemplate constant


bd doctor: add configuration value validation

Currently bd doctor checks for missing config files but doesn't validate config values. Add validation for: invalid priority values, malformed sync intervals, invalid branch names, unsupported storage backends, etc. This would catch misconfigurations before they cause runtime errors.