fix: improve JSONL-only mode detection and error messages (GH #534)

- Add JSONL-only mode detection in ensureStoreActive() with context-aware
  error messages that suggest correct actions based on project state
- Improve error messages in main.go to detect JSONL presence and suggest
  appropriate solutions (bd init, --no-db flag, or config.yaml setting)
- Update documentation to use issues.jsonl as canonical filename:
  - AGENT_INSTRUCTIONS.md, README.md, resolve-beads-conflict.md
  - docs/GIT_INTEGRATION.md
- Update hook template comments to clarify issues.jsonl is canonical
  while maintaining backward compatibility for beads.jsonl

Fixes #534

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-13 11:00:41 -08:00
parent e01b7412d9
commit 5d71ca6356
9 changed files with 847 additions and 811 deletions

View File

@@ -1,13 +1,13 @@
--- ---
description: How to resolve merge conflicts in .beads/beads.jsonl description: How to resolve merge conflicts in .beads/issues.jsonl
--- ---
# Resolving `beads.jsonl` Merge Conflicts # Resolving `issues.jsonl` Merge Conflicts
If you encounter a merge conflict in `.beads/beads.jsonl` that doesn't have standard git conflict markers (or if `bd merge` failed automatically), follow this procedure. If you encounter a merge conflict in `.beads/issues.jsonl` that doesn't have standard git conflict markers (or if `bd merge` failed automatically), follow this procedure.
## 1. Identify the Conflict ## 1. Identify the Conflict
Check if `beads.jsonl` is in conflict: Check if `issues.jsonl` is in conflict:
```powershell ```powershell
git status git status
``` ```
@@ -20,9 +20,9 @@ Git stores three versions of conflicted files in its index:
Extract them to temporary files: Extract them to temporary files:
```powershell ```powershell
git show :1:.beads/beads.jsonl > beads.base.jsonl git show :1:.beads/issues.jsonl > beads.base.jsonl
git show :2:.beads/beads.jsonl > beads.ours.jsonl git show :2:.beads/issues.jsonl > beads.ours.jsonl
git show :3:.beads/beads.jsonl > beads.theirs.jsonl git show :3:.beads/issues.jsonl > beads.theirs.jsonl
``` ```
## 3. Run `bd merge` Manually ## 3. Run `bd merge` Manually
@@ -43,13 +43,13 @@ Optionally, verify the content (e.g., check for missing IDs if you suspect data
## 5. Apply the Merge ## 5. Apply the Merge
Overwrite the conflicted file with the resolved version: Overwrite the conflicted file with the resolved version:
```powershell ```powershell
cp beads.merged.jsonl .beads/beads.jsonl cp beads.merged.jsonl .beads/issues.jsonl
``` ```
## 6. Cleanup and Continue ## 6. Cleanup and Continue
Stage the resolved file and continue the merge: Stage the resolved file and continue the merge:
```powershell ```powershell
git add .beads/beads.jsonl git add .beads/issues.jsonl
git merge --continue git merge --continue
``` ```

View File

@@ -4,7 +4,7 @@
# bd (beads) pre-commit hook # bd (beads) pre-commit hook
# #
# This hook ensures that any pending bd issue changes are flushed to # This hook ensures that any pending bd issue changes are flushed to
# .beads/beads.jsonl before the commit is created, preventing the # .beads/issues.jsonl before the commit is created, preventing the
# race condition where daemon auto-flush fires after the commit. # race condition where daemon auto-flush fires after the commit.
# #
# Installation: # Installation:
@@ -35,7 +35,7 @@ if ! bd sync --flush-only >/dev/null 2>&1; then
exit 1 exit 1
fi fi
# Stage all tracked JSONL files (beads.jsonl, issues.jsonl for backward compat, deletions.jsonl for deletion propagation) # Stage all tracked JSONL files (issues.jsonl is canonical, beads.jsonl for backward compat, deletions.jsonl for deletion propagation)
# git add is harmless if file doesn't exist # git add is harmless if file doesn't exist
for f in .beads/beads.jsonl .beads/issues.jsonl .beads/deletions.jsonl; do for f in .beads/beads.jsonl .beads/issues.jsonl .beads/deletions.jsonl; do
[ -f "$f" ] && git add "$f" 2>/dev/null || true [ -f "$f" ] && git add "$f" 2>/dev/null || true

File diff suppressed because one or more lines are too long

View File

@@ -79,7 +79,7 @@ The 30-second debounce provides a **transaction window** for batch operations -
**Git worktrees**: Enhanced support with shared database architecture. Use `bd --no-daemon` if daemon warnings appear. See [docs/GIT_INTEGRATION.md](docs/GIT_INTEGRATION.md). **Git worktrees**: Enhanced support with shared database architecture. Use `bd --no-daemon` if daemon warnings appear. See [docs/GIT_INTEGRATION.md](docs/GIT_INTEGRATION.md).
**Merge conflicts**: Rare with hash IDs. If conflicts occur, use `git checkout --theirs/.beads/beads.jsonl` and `bd import`. See [docs/GIT_INTEGRATION.md](docs/GIT_INTEGRATION.md). **Merge conflicts**: Rare with hash IDs. If conflicts occur, use `git checkout --theirs/.beads/issues.jsonl` and `bd import`. See [docs/GIT_INTEGRATION.md](docs/GIT_INTEGRATION.md).
## Landing the Plane ## Landing the Plane
@@ -95,9 +95,9 @@ The 30-second debounce provides a **transaction window** for batch operations -
# Pull first to catch any remote changes # Pull first to catch any remote changes
git pull --rebase git pull --rebase
# If conflicts in .beads/beads.jsonl, resolve thoughtfully: # If conflicts in .beads/issues.jsonl, resolve thoughtfully:
# - git checkout --theirs .beads/beads.jsonl (accept remote) # - git checkout --theirs .beads/issues.jsonl (accept remote)
# - bd import -i .beads/beads.jsonl (re-import) # - bd import -i .beads/issues.jsonl (re-import)
# - Or manual merge, then import # - Or manual merge, then import
# Sync the database (exports to JSONL, commits) # Sync the database (exports to JSONL, commits)
@@ -145,9 +145,9 @@ bd close bd-42 bd-43 --reason "Completed" --json
# 4. PUSH TO REMOTE - MANDATORY, NO STOPPING BEFORE THIS IS DONE # 4. PUSH TO REMOTE - MANDATORY, NO STOPPING BEFORE THIS IS DONE
git pull --rebase git pull --rebase
# If conflicts in .beads/beads.jsonl, resolve thoughtfully: # If conflicts in .beads/issues.jsonl, resolve thoughtfully:
# - git checkout --theirs .beads/beads.jsonl (accept remote) # - git checkout --theirs .beads/issues.jsonl (accept remote)
# - bd import -i .beads/beads.jsonl (re-import) # - bd import -i .beads/issues.jsonl (re-import)
# - Or manual merge, then import # - Or manual merge, then import
bd sync # Export/import/commit bd sync # Export/import/commit
git push # MANDATORY - THE PLANE IS STILL IN THE AIR UNTIL THIS SUCCEEDS git push # MANDATORY - THE PLANE IS STILL IN THE AIR UNTIL THIS SUCCEEDS

View File

@@ -166,7 +166,7 @@ echo -e "\nBEFORE ANYTHING ELSE: run 'bd onboard' and follow the instructions" >
```bash ```bash
git config merge.beads.driver "bd merge %A %O %A %B" git config merge.beads.driver "bd merge %A %O %A %B"
git config merge.beads.name "bd JSONL merge driver" git config merge.beads.name "bd JSONL merge driver"
echo ".beads/beads.jsonl merge=beads" >> .gitattributes echo ".beads/issues.jsonl merge=beads" >> .gitattributes
``` ```
### Files Created by `bd init` ### Files Created by `bd init`
@@ -175,7 +175,7 @@ echo ".beads/beads.jsonl merge=beads" >> .gitattributes
**Should be committed to git:** **Should be committed to git:**
- `.gitattributes` - Configures git merge driver for intelligent JSONL merging (critical for team collaboration) - `.gitattributes` - Configures git merge driver for intelligent JSONL merging (critical for team collaboration)
- `.beads/beads.jsonl` - Issue data in JSONL format (source of truth, synced via git) - `.beads/issues.jsonl` - Issue data in JSONL format (source of truth, synced via git)
- `.beads/deletions.jsonl` - Deletion manifest for cross-clone propagation (tracks deleted issues) - `.beads/deletions.jsonl` - Deletion manifest for cross-clone propagation (tracks deleted issues)
- `.beads/config.yaml` - Repository configuration template - `.beads/config.yaml` - Repository configuration template
- `.beads/README.md` - Documentation about beads for repository visitors - `.beads/README.md` - Documentation about beads for repository visitors

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"github.com/steveyegge/beads/internal/beads" "github.com/steveyegge/beads/internal/beads"
@@ -62,7 +63,25 @@ func ensureStoreActive() error {
if found := beads.FindDatabasePath(); found != "" { if found := beads.FindDatabasePath(); found != "" {
dbPath = found dbPath = found
} else { } else {
return fmt.Errorf("no beads database found. Hint: run 'bd init' in this directory") // Check if this is a JSONL-only project (bd-534)
beadsDir := beads.FindBeadsDir()
if beadsDir != "" {
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
if _, err := os.Stat(jsonlPath); err == nil {
// JSONL exists - check if no-db mode is configured
if isNoDbModeConfigured(beadsDir) {
return fmt.Errorf("this project uses JSONL-only mode (no SQLite database).\n" +
"Hint: use 'bd --no-db <command>' or set 'no-db: true' in config.yaml")
}
// JSONL exists but no-db not configured - fresh clone scenario
return fmt.Errorf("found JSONL file but no database: %s\n"+
"Hint: run 'bd init' to create the database and import issues,\n"+
" or use 'bd --no-db' for JSONL-only mode", jsonlPath)
}
}
return fmt.Errorf("no beads database found.\n" +
"Hint: run 'bd init' to create a database in the current directory,\n" +
" or use 'bd --no-db' for JSONL-only mode")
} }
} }

View File

@@ -329,8 +329,25 @@ var rootCmd = &cobra.Command{
// - import: auto-initializes database if missing // - import: auto-initializes database if missing
// - setup: creates editor integration files (no DB needed) // - setup: creates editor integration files (no DB needed)
if cmd.Name() != "import" && cmd.Name() != "setup" { if cmd.Name() != "import" && cmd.Name() != "setup" {
// No database found - error out instead of falling back to ~/.beads // No database found - provide context-aware error message (bd-534)
fmt.Fprintf(os.Stderr, "Error: no beads database found\n") fmt.Fprintf(os.Stderr, "Error: no beads database found\n")
// Check if JSONL exists without no-db mode configured
if beadsDir != "" {
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
if _, err := os.Stat(jsonlPath); err == nil {
// JSONL exists but no-db mode not configured
fmt.Fprintf(os.Stderr, "\nFound JSONL file: %s\n", jsonlPath)
fmt.Fprintf(os.Stderr, "This looks like a fresh clone or JSONL-only project.\n\n")
fmt.Fprintf(os.Stderr, "Options:\n")
fmt.Fprintf(os.Stderr, " • Run 'bd init' to create database and import issues\n")
fmt.Fprintf(os.Stderr, " • Use 'bd --no-db %s' for JSONL-only mode\n", cmd.Name())
fmt.Fprintf(os.Stderr, " • Add 'no-db: true' to .beads/config.yaml for permanent JSONL-only mode\n")
os.Exit(1)
}
}
// Generic error - no beads directory or JSONL found
fmt.Fprintf(os.Stderr, "Hint: run 'bd init' to create a database in the current directory\n") fmt.Fprintf(os.Stderr, "Hint: run 'bd init' to create a database in the current directory\n")
fmt.Fprintf(os.Stderr, " or use 'bd --no-db' to work with JSONL only (no SQLite)\n") fmt.Fprintf(os.Stderr, " or use 'bd --no-db' to work with JSONL only (no SQLite)\n")
fmt.Fprintf(os.Stderr, " or set BEADS_DIR to point to your .beads directory\n") fmt.Fprintf(os.Stderr, " or set BEADS_DIR to point to your .beads directory\n")

View File

@@ -4,7 +4,7 @@
# bd (beads) pre-commit hook # bd (beads) pre-commit hook
# #
# This hook ensures that any pending bd issue changes are flushed to # This hook ensures that any pending bd issue changes are flushed to
# .beads/beads.jsonl before the commit is created, preventing the # .beads/issues.jsonl before the commit is created, preventing the
# race condition where daemon auto-flush fires after the commit. # race condition where daemon auto-flush fires after the commit.
# #
# When sync-branch is configured in config.yaml, .beads changes are committed # When sync-branch is configured in config.yaml, .beads changes are committed
@@ -52,7 +52,7 @@ if ! bd sync --flush-only >/dev/null 2>&1; then
exit 1 exit 1
fi fi
# Stage all tracked JSONL files (beads.jsonl, issues.jsonl for backward compat, deletions.jsonl for deletion propagation) # Stage all tracked JSONL files (issues.jsonl is canonical, beads.jsonl for backward compat, deletions.jsonl for deletion propagation)
# For worktrees, .beads is in the main repo's working tree, not the worktree, # For worktrees, .beads is in the main repo's working tree, not the worktree,
# so we can't use git add. Skip staging for worktrees. # so we can't use git add. Skip staging for worktrees.
if [ "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)" ]; then if [ "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)" ]; then

View File

@@ -100,7 +100,7 @@ bd automatically detects worktrees and shows prominent warnings if daemon mode i
### When Conflicts Occur ### When Conflicts Occur
Git conflicts in `.beads/beads.jsonl` happen when: Git conflicts in `.beads/issues.jsonl` happen when:
- **Same issue modified on both branches** (different timestamps/fields) - **Same issue modified on both branches** (different timestamps/fields)
- This is a **same-issue update conflict**, not an ID collision - This is a **same-issue update conflict**, not an ID collision
- Conflicts are rare in practice since hash IDs prevent structural collisions - Conflicts are rare in practice since hash IDs prevent structural collisions
@@ -111,9 +111,9 @@ bd automatically detects conflict markers and shows clear resolution steps:
```bash ```bash
# bd import rejects files with conflict markers # bd import rejects files with conflict markers
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
# Error: JSONL file contains git conflict markers # Error: JSONL file contains git conflict markers
# Resolve with: git checkout --theirs .beads/beads.jsonl # Resolve with: git checkout --theirs .beads/issues.jsonl
# Validate for conflicts # Validate for conflicts
bd validate --checks=conflicts bd validate --checks=conflicts
@@ -124,22 +124,22 @@ Conflict markers detected: `<<<<<<<`, `=======`, `>>>>>>>`
### Resolution Workflow ### Resolution Workflow
```bash ```bash
# After git merge creates conflict in .beads/beads.jsonl # After git merge creates conflict in .beads/issues.jsonl
# Option 1: Accept their version (remote) # Option 1: Accept their version (remote)
git checkout --theirs .beads/beads.jsonl git checkout --theirs .beads/issues.jsonl
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
# Option 2: Keep our version (local) # Option 2: Keep our version (local)
git checkout --ours .beads/beads.jsonl git checkout --ours .beads/issues.jsonl
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
# Option 3: Manual resolution in editor # Option 3: Manual resolution in editor
# Edit .beads/beads.jsonl to remove conflict markers # Edit .beads/issues.jsonl to remove conflict markers
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
# Commit the merge # Commit the merge
git add .beads/beads.jsonl git add .beads/issues.jsonl
git commit git commit
``` ```
@@ -170,7 +170,7 @@ git config merge.beads.driver "bd merge %A %O %A %B"
git config merge.beads.name "bd JSONL merge driver" git config merge.beads.name "bd JSONL merge driver"
# .gitattributes entry added: # .gitattributes entry added:
# .beads/beads.jsonl merge=beads # .beads/issues.jsonl merge=beads
``` ```
### Manual Setup ### Manual Setup
@@ -180,7 +180,7 @@ git config merge.beads.name "bd JSONL merge driver"
```bash ```bash
git config merge.beads.driver "bd merge %A %O %A %B" git config merge.beads.driver "bd merge %A %O %A %B"
git config merge.beads.name "bd JSONL merge driver" git config merge.beads.name "bd JSONL merge driver"
echo ".beads/beads.jsonl merge=beads" >> .gitattributes echo ".beads/issues.jsonl merge=beads" >> .gitattributes
``` ```
### How It Works ### How It Works
@@ -532,20 +532,20 @@ export BEADS_NO_DAEMON=1 # Direct mode
``` ```
# Intelligent merge driver for JSONL (auto-configured by bd init) # Intelligent merge driver for JSONL (auto-configured by bd init)
.beads/beads.jsonl merge=beads .beads/issues.jsonl merge=beads
# Treat JSONL as text for diffs # Treat JSONL as text for diffs
.beads/*.jsonl text diff .beads/*.jsonl text diff
``` ```
This file is automatically created by `bd init` and is essential for: This file is automatically created by `bd init` and is essential for:
- Preventing spurious merge conflicts in `.beads/beads.jsonl` - Preventing spurious merge conflicts in `.beads/issues.jsonl`
- Enabling field-level 3-way merging instead of line-by-line - Enabling field-level 3-way merging instead of line-by-line
- Ensuring all team members get intelligent JSONL merging - Ensuring all team members get intelligent JSONL merging
### Git LFS Considerations ### Git LFS Considerations
**Do NOT use Git LFS for `.beads/beads.jsonl`:** **Do NOT use Git LFS for `.beads/issues.jsonl`:**
- JSONL needs intelligent merge (doesn't work with LFS) - JSONL needs intelligent merge (doesn't work with LFS)
- File size stays reasonable (<1MB per 10K issues) - File size stays reasonable (<1MB per 10K issues)
- Text diffs are valuable for review - Text diffs are valuable for review
@@ -563,7 +563,7 @@ WARN Database timestamp older than JSONL, importing...
```bash ```bash
# Normal after git pull - auto-import handles it # Normal after git pull - auto-import handles it
# If stuck, force import: # If stuck, force import:
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
``` ```
### Issue: "Database is ahead of JSONL" ### Issue: "Database is ahead of JSONL"
@@ -583,7 +583,7 @@ bd sync
### Issue: Merge conflicts every time ### Issue: Merge conflicts every time
**Symptoms:** **Symptoms:**
- Git merge always creates conflicts in `.beads/beads.jsonl` - Git merge always creates conflicts in `.beads/issues.jsonl`
- Merge driver not being used - Merge driver not being used
**Solutions:** **Solutions:**
@@ -596,7 +596,7 @@ bd init --skip-db # Only reconfigure git, don't touch database
# Verify .gitattributes # Verify .gitattributes
grep "beads.jsonl" .gitattributes grep "beads.jsonl" .gitattributes
# Expected: .beads/beads.jsonl merge=beads # Expected: .beads/issues.jsonl merge=beads
``` ```
### Issue: Changes not syncing to other workspaces ### Issue: Changes not syncing to other workspaces
@@ -613,7 +613,7 @@ git push
# Agent B: Force import # Agent B: Force import
git pull git pull
bd import -i .beads/beads.jsonl bd import -i .beads/issues.jsonl
# Check git hooks installed (prevent future issues) # Check git hooks installed (prevent future issues)
./examples/git-hooks/install.sh ./examples/git-hooks/install.sh