diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 97c0806a..44b3006d 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -36,8 +36,9 @@ {"id":"bd-4y4g","title":"Bump version in all files","description":"Run ./scripts/bump-version.sh {{version}} to update 10 version files. Then run with --commit after info.go is updated.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T22:43:01.859728-08:00","updated_at":"2025-12-18T22:46:24.537336-08:00","closed_at":"2025-12-18T22:46:24.537336-08:00","dependencies":[{"issue_id":"bd-4y4g","depends_on_id":"bd-qqc","type":"parent-child","created_at":"2025-12-18T22:43:16.623724-08:00","created_by":"daemon"},{"issue_id":"bd-4y4g","depends_on_id":"bd-8v2","type":"blocks","created_at":"2025-12-18T22:43:20.823329-08:00","created_by":"daemon"}]} {"id":"bd-56x","title":"Review PR #514: fix plugin install docs","description":"Review and merge PR #514 from aspiers. This PR fixes incorrect docs for installing Claude Code plugin from source in docs/PLUGIN.md. Clarifies shell vs Claude Code commands and fixes the . vs ./beads argument issue. URL: https://github.com/anthropics/beads/pull/514","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-13T08:15:16.865354+11:00","updated_at":"2025-12-13T07:07:19.729213-08:00","closed_at":"2025-12-13T07:07:19.729213-08:00"} {"id":"bd-581b80b3","title":"bd find-duplicates - AI-powered duplicate detection","description":"Find semantically duplicate issues.\n\nApproaches:\n1. Mechanical: Exact title/description matching\n2. Embeddings: Cosine similarity (cheap, scalable)\n3. AI: LLM-based semantic comparison (expensive, accurate)\n\nUses embeddings by default for \u003e100 issues.\n\nFiles: cmd/bd/find_duplicates.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T20:49:49.126801-07:00","updated_at":"2025-12-17T22:58:34.563511-08:00","closed_at":"2025-12-17T22:58:34.563511-08:00","close_reason":"Closed"} +{"id":"bd-589x","title":"HANDOFF: Version 0.30.7 release in progress","description":"## Context\nDoing a 0.30.7 patch release with bug fixes.\n\n## What's done\n- Fixed #657: bd graph nil pointer crash (graph.go:102)\n- Fixed #652: Windows npm installer file lock (postinstall.js)\n- Updated CHANGELOG.md and info.go\n- Pushed to main, CI running (run 20390861825)\n- Created version-bump molecule template (bd-6s61) and instantiated for 0.30.7 (bd-8pyn)\n\n## In progress\nMolecule bd-8pyn has 3 remaining tasks:\n - bd-dxo7: Wait for CI to pass\n - bd-7l70: Verify release artifacts \n - bd-5c91: Update local installation\n\n## Check CI\n gh run list --repo steveyegge/beads --limit 1\n gh run view 20390861825 --repo steveyegge/beads\n\n## New feature filed\nbd-n777: Timer beads for scheduled agent callbacks\nDesign for Deacon-managed timers that can interrupt agents via tmux\n\n## Resume commands\n bd --no-daemon show bd-8pyn\n gh run list --repo steveyegge/beads --limit 1","status":"open","priority":2,"issue_type":"message","assignee":"beads/dave","created_at":"2025-12-19T23:06:14.902334-08:00","updated_at":"2025-12-19T23:06:14.902334-08:00","sender":"Steve Yegge","ephemeral":true} {"id":"bd-5b6e","title":"Add tests for helper functions (GetDirtyIssueHash, GetAllDependencyRecords, export hashes)","description":"Several utility functions have 0% coverage:\n- GetDirtyIssueHash (dirty.go)\n- GetAllDependencyRecords (dependencies.go)\n- GetExportHash, SetExportHash, ClearAllExportHashes (hash.go)\n\nThese are lower priority but should have basic coverage.","status":"open","priority":4,"issue_type":"task","created_at":"2025-11-01T22:40:58.989976-07:00","updated_at":"2025-11-01T22:40:58.989976-07:00"} -{"id":"bd-5c91","title":"Update local installation","description":"Run install script or brew upgrade to get new version locally: curl -fsSL .../install.sh | bash","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:48.649211-08:00","updated_at":"2025-12-19T22:56:48.649211-08:00","dependencies":[{"issue_id":"bd-5c91","depends_on_id":"bd-8pyn","type":"parent-child","created_at":"2025-12-19T22:56:48.651449-08:00","created_by":"stevey"},{"issue_id":"bd-5c91","depends_on_id":"bd-7l70","type":"blocks","created_at":"2025-12-19T22:56:48.651763-08:00","created_by":"stevey"}]} +{"id":"bd-5c91","title":"Update local installation","description":"Run install script or brew upgrade to get new version locally: curl -fsSL .../install.sh | bash","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:48.649211-08:00","updated_at":"2025-12-19T22:56:48.649211-08:00","dependencies":[{"issue_id":"bd-5c91","depends_on_id":"bd-8pyn","type":"parent-child","created_at":"2025-12-19T22:56:48.651449-08:00","created_by":"stevey"}]} {"id":"bd-5qim","title":"Optimize GetReadyWork performance - 752ms on 10K database (target: \u003c50ms)","description":"","notes":"# Performance Analysis (10K Issue Database)\n\nAnalyzed using CPU profiles from benchmark suite on Apple M2 Pro.\n\n## Operation Performance\n\n| Operation | Time | Allocations | Memory |\n|----------------------------------|---------|-------------|--------|\n| bd ready (GetReadyWork) | ~752ms | 167,466 | 16MB |\n| bd list (SearchIssues no filter) | ~11.6ms | 89,214 | 5.8MB |\n| bd list (SearchIssues filtered) | ~9.2ms | 62,365 | 3.5MB |\n| bd create (CreateIssue) | ~2.6ms | 146 | 8.6KB |\n| bd update (UpdateIssue) | ~0.32ms | 364 | 15KB |\n| bd close (UpdateIssue) | ~0.32ms | 364 | 15KB |\n\n**Target: \u003c50ms for all operations on 10K database**\n\n**Current issue: GetReadyWork is 15x over target (752ms vs 50ms)**\n\n## Root Cause\n\nGetReadyWork (internal/storage/sqlite/ready.go:90-128) uses recursive CTE to propagate blocking:\n- 65x slower than SearchIssues\n- Recalculates entire blocked issue tree on every call\n- Algorithm:\n 1. Find directly blocked issues via 'blocks' dependencies\n 2. Recursively propagate blockage to descendants (max depth: 50)\n 3. Exclude all blocked issues from results\n\n## CPU Profile Analysis\n\n- Database syscalls (pthread_cond_signal, syscall6): ~75%\n- SQLite engine overhead: inherent to recursive CTE\n- Application code (query construction): \u003c1%\n\n**Bottleneck is the recursive CTE query execution, not application code.**\n\n## Optimization Recommendations\n\n### High Impact (Likely to achieve \u003c50ms target)\n\n1. **Cache blocked issue calculation**\n - Add `blocked_issues` table updated on dependency changes\n - Trade write complexity for read speed (ready called \u003e\u003e dependency changes)\n - Eliminates recursive CTE on every read\n\n2. **Add/verify database indexes**\n ```sql\n CREATE INDEX IF NOT EXISTS idx_dependencies_blocked \n ON dependencies(issue_id, type, depends_on_id);\n CREATE INDEX IF NOT EXISTS idx_issues_status \n ON issues(status);\n ```\n\n### Medium Impact\n\n3. **Reduce allocations** (167K allocations for GetReadyWork)\n - Profile `scanIssues()` for object pooling opportunities\n - Reuse slice capacity for repeated calls\n\n### Low Impact (Not recommended)\n- Query optimization for CRUD operations (already \u003c3ms)\n- Connection pooling tuning (not showing in profiles)\n\n## Verification\n\nRun benchmarks to validate optimization:\n```bash\nmake bench-quick\ngo tool pprof -http=:8080 internal/storage/sqlite/bench-cpu-*.prof\n```\n\nProfile files automatically generated in `internal/storage/sqlite/`.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-14T09:02:46.507526-08:00","updated_at":"2025-12-17T23:13:40.534258-08:00","closed_at":"2025-12-17T16:21:37.918868-08:00"} {"id":"bd-6fe4622f","title":"Remove unreachable utility functions","description":"Several small utility functions are unreachable:\n\nFiles to clean:\n1. `internal/storage/sqlite/hash.go` - `computeIssueContentHash` (line 17)\n - Check if entire file can be deleted if only contains this function\n\n2. `internal/config/config.go` - `FileUsed` (line 151)\n - Delete unused config helper\n\n3. `cmd/bd/git_sync_test.go` - `verifyIssueOpen` (line 300)\n - Delete dead test helper\n\n4. `internal/compact/haiku.go` - `HaikuClient.SummarizeTier2` (line 81)\n - Tier 2 summarization not implemented\n - Options: implement feature OR delete method\n\nImpact: Removes 50-100 LOC depending on decisions","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.434573-07:00","updated_at":"2025-12-17T22:58:34.563993-08:00","closed_at":"2025-12-17T22:58:34.563993-08:00","close_reason":"Closed"} {"id":"bd-6gd","title":"Remove legacy MCP Agent Mail integration","description":"## Summary\n\nRemove the legacy MCP Agent Mail system that requires an external HTTP server. Keep the native `bd mail` system which stores messages as git-synced issues.\n\n## Background\n\nTwo mail systems exist in the codebase:\n1. **Legacy Agent Mail** (`bd message`) - External server dependency, complex setup\n2. **Native bd mail** (`bd mail`) - Built-in, git-synced, no dependencies\n\nThe legacy system causes confusion and is no longer needed. Gas Town's Town Mail will use the native `bd mail` system.\n\n## Files to Delete\n\n### CLI Command\n- [ ] `cmd/bd/message.go` - The `bd message` command implementation\n\n### MCP Integration\n- [ ] `integrations/beads-mcp/src/beads_mcp/mail.py` - HTTP wrapper for Agent Mail server\n- [ ] `integrations/beads-mcp/src/beads_mcp/mail_tools.py` - MCP tool definitions\n- [ ] `integrations/beads-mcp/tests/test_mail.py` - Tests for legacy mail\n\n### Documentation\n- [ ] `docs/AGENT_MAIL.md`\n- [ ] `docs/AGENT_MAIL_QUICKSTART.md`\n- [ ] `docs/AGENT_MAIL_DEPLOYMENT.md`\n- [ ] `docs/AGENT_MAIL_MULTI_WORKSPACE_SETUP.md`\n- [ ] `docs/adr/002-agent-mail-integration.md`\n\n## Code to Update\n\n- [ ] Remove `message` command registration from `cmd/bd/main.go`\n- [ ] Remove mail tool imports/registration from MCP server `__init__.py` or `server.py`\n- [ ] Check for any other references to Agent Mail in the codebase\n\n## Verification\n\n- [ ] `bd message` command no longer exists\n- [ ] `bd mail` command still works\n- [ ] MCP server starts without errors\n- [ ] Tests pass\n","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-17T23:04:04.099935-08:00","updated_at":"2025-12-17T23:13:24.128752-08:00","closed_at":"2025-12-17T23:13:24.128752-08:00","close_reason":"Removed legacy MCP Agent Mail integration. Kept native bd mail system."} @@ -53,7 +54,6 @@ {"id":"bd-7di","title":"worktree: any bd command is slow","description":"in a git worktree any bd command is slow, with a 2-3s pause before any results are shown. The identical command with `--no-daemon` is near instant.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-05T15:33:42.924618693-07:00","updated_at":"2025-12-05T15:33:42.924618693-07:00"} {"id":"bd-7h5","title":"Add pinned field to issue schema","description":"Add boolean 'pinned' field to the issue schema. When true, the issue is marked as a persistent context marker that should not be treated as a work item.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T23:33:26.767247-08:00","updated_at":"2025-12-19T00:08:59.854605-08:00","closed_at":"2025-12-19T00:08:59.854605-08:00","close_reason":"Implemented by polecat Slit - pushed to main","dependencies":[{"issue_id":"bd-7h5","depends_on_id":"bd-0vg","type":"blocks","created_at":"2025-12-18T23:33:55.98635-08:00","created_by":"daemon"}]} {"id":"bd-7h7","title":"bd init should stop running daemon to avoid stale cache","description":"When running bd init, any running daemon continues with stale cached data, causing bd stats and other commands to show old counts.\n\nRepro:\n1. Have daemon running with 788 issues cached\n2. Clean JSONL to 128 issues, delete db, run bd init\n3. bd stats still shows 788 (daemon cache)\n4. Must manually run bd daemon --stop\n\nFix: bd init should automatically stop any running daemon before reinitializing.","status":"tombstone","priority":2,"issue_type":"bug","created_at":"2025-12-16T13:26:47.117226-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} -{"id":"bd-7l70","title":"Verify release artifacts","description":"Check GitHub releases page - binaries for darwin/linux/windows should be available","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:48.649793-08:00","updated_at":"2025-12-19T22:56:48.649793-08:00","dependencies":[{"issue_id":"bd-7l70","depends_on_id":"bd-8pyn","type":"parent-child","created_at":"2025-12-19T22:56:48.653842-08:00","created_by":"stevey"},{"issue_id":"bd-7l70","depends_on_id":"bd-dxo7","type":"blocks","created_at":"2025-12-19T22:56:48.65417-08:00","created_by":"stevey"}]} {"id":"bd-7m16","title":"GH#519: bd sync fails when sync.branch is currently checked-out branch","description":"bd sync tries to create worktree for sync.branch even when already on that branch. Should commit directly instead. See GitHub issue #519.","status":"tombstone","priority":2,"issue_type":"bug","created_at":"2025-12-16T01:03:36.613211-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-7tuu","title":"Commit and push release","description":"git add -A \u0026\u0026 git commit \u0026\u0026 git push to trigger CI","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:02.053382-08:00","updated_at":"2025-12-19T22:56:02.053382-08:00","dependencies":[{"issue_id":"bd-7tuu","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:15.021087-08:00","created_by":"daemon"},{"issue_id":"bd-7tuu","depends_on_id":"bd-hw3w","type":"blocks","created_at":"2025-12-19T22:56:23.291591-08:00","created_by":"daemon"}]} {"id":"bd-7yg","title":"Git merge driver uses invalid placeholders (%L, %R instead of %A, %B)","description":"## Problem\n\nThe beads git merge driver is configured with invalid Git placeholders:\n\n```\ngit config merge.beads.driver \"bd merge %A %O %L %R\"\n```\n\nGit doesn't recognize `%L` or `%R` as valid merge driver placeholders. The valid placeholders are:\n- `%O` = base (common ancestor)\n- `%A` = current version (ours)\n- `%B` = other version (theirs)\n\n## Impact\n\n- Affects ALL users when they have `.beads/beads.jsonl` merge conflicts\n- Automatic JSONL merge fails with error: \"error reading left file: failed to open file: open 7: no such file or directory\"\n- Users must manually resolve conflicts instead of getting automatic merge\n\n## Root Cause\n\nThe `bd init` command (or wherever the merge driver is configured) is using non-standard placeholders. When Git encounters `%L` and `%R`, it either passes them literally or interprets them incorrectly.\n\n## Fix\n\nUpdate the merge driver configuration to:\n```\ngit config merge.beads.driver \"bd merge %A %O %A %B\"\n```\n\nWhere:\n- 1st `%A` = output file (current file, will be overwritten)\n- `%O` = base (common ancestor)\n- 2nd `%A` = left/current version\n- `%B` = right/other version\n\n## Action Items\n\n1. Fix `bd init` (or equivalent setup command) to use correct placeholders\n2. Add migration/warning for existing users with misconfigured merge driver\n3. Update documentation with correct merge driver setup\n4. Consider adding validation when `bd init` is run","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-21T19:51:55.747608-05:00","updated_at":"2025-12-17T23:13:40.532368-08:00","closed_at":"2025-12-17T17:24:52.678668-08:00"} @@ -124,7 +124,6 @@ {"id":"bd-dsdh","title":"Document sync.branch 'always dirty' working tree behavior","description":"## Context\n\nWhen sync.branch is configured, the .beads/issues.jsonl file in main's working tree is ALWAYS dirty. This is by design:\n\n1. bd sync commits to beads-sync branch (via worktree)\n2. bd sync copies JSONL to main's working tree (so CLI commands work)\n3. This copy is NOT committed to main (to reduce commit noise)\n\nContributors who watch main branch history pushed for sync.branch to avoid constant beads commit noise. But users need to understand the trade-off.\n\n## Documentation Needed\n\nUpdate README.md sync.branch section with:\n\n1. **Clear explanation** of why .beads/ is always dirty on main\n2. **\"Be Zen about it\"** - this is expected, not a bug\n3. **Workflow options:**\n - Accept dirty state, use `bd sync --merge` periodically to snapshot to main\n - Or disable sync.branch if clean working tree is more important\n4. **Shell alias tip** to hide beads from git status:\n ```bash\n alias gs='git status -- \":!.beads/\"'\n ```\n5. **When to merge**: releases, milestones, or periodic snapshots\n\n## Related\n\n- bd-7b7h: Fix that allows bd sync --merge to work with dirty .beads/\n- bd-elqd: Investigation that identified this as expected behavior","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T23:16:12.253559-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} {"id":"bd-dsp","title":"Test stdin body-file","description":"","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-17T17:27:32.098806-08:00","updated_at":"2025-12-17T17:28:33.832749-08:00","closed_at":"2025-12-17T17:28:33.832749-08:00"} {"id":"bd-dwh","title":"Implement or remove ExpectExit/ExpectStdout verification fields","description":"The Verification struct in internal/types/workflow.go has ExpectExit and ExpectStdout fields that are never used by workflowVerifyCmd. Either implement the functionality or remove the dead fields.","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-17T22:23:02.708627-08:00","updated_at":"2025-12-17T22:34:07.300348-08:00","closed_at":"2025-12-17T22:34:07.300348-08:00"} -{"id":"bd-dxo7","title":"Wait for CI to pass","description":"Monitor GitHub Actions - all checks must pass before release artifacts are built","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:48.649505-08:00","updated_at":"2025-12-19T22:56:48.649505-08:00","dependencies":[{"issue_id":"bd-dxo7","depends_on_id":"bd-8pyn","type":"parent-child","created_at":"2025-12-19T22:56:48.652685-08:00","created_by":"stevey"},{"issue_id":"bd-dxo7","depends_on_id":"bd-zclk","type":"blocks","created_at":"2025-12-19T22:56:48.653035-08:00","created_by":"stevey"}]} {"id":"bd-dyy","title":"Review PR #513: fix hooks install docs","description":"Review and merge PR #513 from aspiers. This PR fixes incorrect docs for how to install git hooks - updates README to use bd hooks install instead of removed install.sh. Simple 1-line change. URL: https://github.com/anthropics/beads/pull/513","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-13T08:15:14.838772+11:00","updated_at":"2025-12-13T07:07:19.718544-08:00","closed_at":"2025-12-13T07:07:19.718544-08:00"} {"id":"bd-e1085716","title":"bd validate - Comprehensive health check","description":"Run all validation checks in one command.\n\nChecks:\n- Duplicates\n- Orphaned dependencies\n- Test pollution\n- Git conflicts\n\nSupports --fix-all for auto-repair.\n\nDepends on bd-cbed9619.1, bd-0dcea000, bd-31aab707, bd-9826b69a.\n\nFiles: cmd/bd/validate.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T23:05:13.980679-07:00","updated_at":"2025-12-17T22:58:34.562008-08:00","closed_at":"2025-12-17T22:58:34.562008-08:00","close_reason":"Closed"} {"id":"bd-elqd","title":"Systematic bd sync stability investigation","description":"## Context\n\nbd sync has chronic instability issues that have persisted since inception:\n- issues.jsonl is always dirty after push\n- bd sync often creates messes requiring manual cleanup\n- Problems escalating despite accumulated bug fixes\n- Workarounds are getting increasingly draconian\n\n## Goal\n\nSystematically observe and diagnose bd sync failures rather than applying band-aid fixes.\n\n## Approach\n\n1. Start fresh session with latest binary (all fixes applied)\n2. Run bd sync and carefully observe what happens\n3. Document exact sequence of events when things go wrong\n4. File specific issues for each discrete problem identified\n5. Track the root causes, not just symptoms\n\n## Test Environment\n\n- Fresh clone or clean state\n- Latest bd binary with all bug fixes\n- Monitor both local and remote JSONL state\n- Check for timing issues, race conditions, merge conflicts\n\n## Success Criteria\n\n- Identify root causes of sync instability\n- Create actionable issues for each problem\n- Eventually achieve stable bd sync (no manual intervention needed)","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-16T22:57:25.35289-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} @@ -182,6 +181,7 @@ {"id":"bd-n386","title":"Improve test coverage for internal/daemon (27.3% → 60%)","description":"The daemon package has only 27.3% test coverage. The daemon is critical for background operations and reliability.\n\nKey areas needing tests:\n- Daemon autostart logic\n- Socket handling\n- PID file management\n- Health checks\n\nCurrent coverage: 27.3%\nTarget coverage: 60%","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T20:43:00.895238-08:00","updated_at":"2025-12-13T21:01:17.274438-08:00"} {"id":"bd-n3v","title":"Error committing to sync branch: failed to create worktree","description":"\u003e bd sync --no-daemon\n→ Exporting pending changes to JSONL...\n→ Committing changes to sync branch 'beads-sync'...\nError committing to sync branch: failed to create worktree: failed to create worktree parent directory: mkdir /var/home/matt/dev/beads/fix-ci/.git: not a directory","notes":"**Problem Diagnosed**: The `bd sync` command was failing with \"mkdir /var/home/matt/dev/beads/fix-ci/.git: not a directory\" because it was being executed from the wrong directory.\n\n**Root Cause**: The command was run from `/var/home/matt/dev/beads` (where the `fix-ci` worktree exists) instead of the main repository directory `/var/home/matt/dev/beads/main`. Since `fix-ci` is a git worktree with a `.git` file (not directory), the worktree creation logic failed when trying to create `\u003ccurrent_dir\u003e/.git/beads-worktrees/\u003cbranch\u003e`.\n\n**Solution Verified**: Execute `bd sync` from the main repository directory:\n```bash\ncd main \u0026\u0026 bd sync --dry-run\n```\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-05T15:25:24.514998248-07:00","updated_at":"2025-12-05T15:42:32.910166956-07:00"} {"id":"bd-n4td","title":"Add warning when staleness check errors","description":"## Problem\n\nWhen ensureDatabaseFresh() calls CheckStaleness() and it errors (corrupted metadata, permission issues, etc.), we silently proceed with potentially stale data.\n\n**Location:** cmd/bd/staleness.go:27-32\n\n**Scenarios:**\n- Corrupted metadata table\n- Database locked by another process \n- Permission issues reading JSONL file\n- Invalid last_import_time format in DB\n\n## Current Code\n\n```go\nisStale, err := autoimport.CheckStaleness(ctx, store, dbPath)\nif err \\!= nil {\n // If we can't determine staleness, allow operation to proceed\n // (better to show potentially stale data than block user)\n return nil\n}\n```\n\n## Fix\n\n```go\nisStale, err := autoimport.CheckStaleness(ctx, store, dbPath)\nif err \\!= nil {\n fmt.Fprintf(os.Stderr, \"Warning: Could not verify database freshness: %v\\n\", err)\n fmt.Fprintf(os.Stderr, \"Proceeding anyway. Data may be stale.\\n\\n\")\n return nil\n}\n```\n\n## Impact\nMedium - users should know when staleness check fails\n\n## Effort\nEasy - 5 minutes","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-11-20T20:16:34.889997-05:00","updated_at":"2025-12-17T23:13:40.531031-08:00","closed_at":"2025-12-17T19:11:12.950618-08:00","dependencies":[{"issue_id":"bd-n4td","depends_on_id":"bd-2q6d","type":"blocks","created_at":"2025-11-20T20:18:20.154723-05:00","created_by":"stevey","metadata":"{}"}]} +{"id":"bd-n777","title":"Timer beads for scheduled agent callbacks","description":"## Problem\n\nAgents frequently need to wait for external events (CI completion, PR reviews, artifact builds) but have no good mechanism:\n- `sleep N` blocks and is unreliable (often times out at 8+ minutes)\n- Polling wastes context and is easy to forget\n- No way to survive session restarts\n\n## Proposal: Timer Beads\n\nA new bead type or field that represents a scheduled callback:\n\n### Creating timers\n```bash\nbd timer create --in 30s --callback \"Check CI run 12345\" --issue bd-xyz\nbd timer create --at \"2025-12-20T08:00:00\" --callback \"Morning standup\"\nbd timer create --in 5m --on-expire \"tmux send-keys -t dave 'bd show bd-xyz'\"\n```\n\n### Timer storage\n- Store in beads (survives restarts)\n- Fields: `expires_at`, `callback_description`, `on_expire_command`, `linked_issue`\n- Status: pending, fired, cancelled\n\n### Deacon integration\nThe Deacon daemon monitors timer beads:\n1. Wakes on next timer expiry\n2. Executes `on_expire` command (e.g., tmux send-keys to interrupt agent)\n3. Marks timer as fired\n4. Optionally updates linked issue\n\n### Use cases\n- CI monitoring: \"ping me when build completes\"\n- PR reviews: \"check back in 1 hour\"\n- Scheduled tasks: \"remind me at EOD to sync\"\n- Blocking waits: agent registers callback instead of sleeping\n\n## Acceptance criteria\n- [ ] Timer bead type or field design\n- [ ] `bd timer create/list/cancel` commands\n- [ ] Deacon timer monitoring loop\n- [ ] tmux integration for agent interrupts\n- [ ] Survives daemon restarts","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-19T23:05:33.051861-08:00","updated_at":"2025-12-19T23:05:33.051861-08:00"} {"id":"bd-ncwo","title":"Ghost resurrection: remote status:closed wins during git merge","description":"During bd sync, the 3-way git merge sometimes keeps remote's status:closed instead of local's status:tombstone. This causes ghost issues to resurrect even when tombstones exist.\n\nRoot cause: Git 3-way merge doesn't understand tombstone semantics. If base had closed, local changed to tombstone, and remote has closed, git might keep remote's version.\n\nThe early tombstone check in importer.go only prevents CREATION when tombstones exist in DB. But if applyDeletionsFromMerge hard-deletes the tombstones before import runs (because they're not in the merged result), the check doesn't help.\n\nPotential fixes:\n1. Make tombstones 'win' in the beads merge driver (internal/merge/merge.go)\n2. Don't hard-delete tombstones in applyDeletionsFromMerge if they're in the DB\n3. Export tombstones to a separate file that's not subject to merge\n\nGhost issues: bd-cb64c226.*, bd-cbed9619.*","status":"tombstone","priority":1,"issue_type":"bug","created_at":"2025-12-16T22:01:03.56423-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-ndye","title":"mergeDependencies uses union instead of 3-way merge","description":"## Critical Bug\n\nThe `mergeDependencies` function in internal/merge/merge.go performs a UNION of left and right dependencies instead of a proper 3-way merge. This causes removed dependencies to be resurrected.\n\n### Root Cause\n\n```go\n// Current code (lines 795-816):\nfunc mergeDependencies(left, right []Dependency) []Dependency {\n // Just unions left + right\n // NEVER REMOVES anything\n // Doesn't even look at base!\n}\n```\n\nAnd `mergeIssue` (line 579) doesn't pass `base`:\n```go\nresult.Dependencies = mergeDependencies(left.Dependencies, right.Dependencies)\n```\n\n### Impact\n\nIf:\n- Base has dependency D\n- Left removes D (intentional)\n- Right still has D (stale)\n\nCurrent: D is in result (resurrection!)\nCorrect: Left removed it, D should NOT be in result\n\nThis breaks Gas Town's workflow and data integrity. Closed means closed.\n\n### Fix\n\nChange `mergeDependencies` to take `base` and do proper 3-way merge:\n- If dep was in base and removed by left → exclude (left wins)\n- If dep was in base and removed by right → exclude (right wins)\n- If dep wasn't in base and added by either → include\n- If dep was in base and both still have it → include\n\nKey principle: **REMOVALS ARE AUTHORITATIVE**\n\n### Files to Change\n\n1. internal/merge/merge.go:\n - `mergeDependencies(left, right)` → `mergeDependencies(base, left, right)`\n - `mergeIssue` line 579: pass `base.Dependencies`\n\n### Related\n\nThis also explains why `ProtectLocalExportIDs` in importer is defined but never used - the protection was never actually implemented.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-12-18T23:15:54.475872-08:00","updated_at":"2025-12-18T23:21:10.709571-08:00","closed_at":"2025-12-18T23:21:10.709571-08:00"} {"id":"bd-nl2","title":"No logging/debugging for tombstone resurrection events","description":"Per the design document bd-zvg Open Question 1: Should resurrection log a warning? Recommendation was Yes. Currently, when an expired tombstone loses to a live issue (resurrection), there is no logging or debugging output. This makes it hard to understand why an issue reappeared. Recommendation: Add optional debug logging when resurrection occurs, e.g., Issue bd-abc resurrected (tombstone expired). Files: internal/merge/merge.go:359-366, 371-378, 400-405, 410-415","status":"open","priority":4,"issue_type":"feature","created_at":"2025-12-05T16:36:52.27525-08:00","updated_at":"2025-12-05T16:36:52.27525-08:00"} @@ -192,6 +192,7 @@ {"id":"bd-ola6","title":"Implement transaction retry logic for SQLITE_BUSY","description":"BEGIN IMMEDIATE fails immediately on SQLITE_BUSY instead of retrying with exponential backoff.\n\nLocation: internal/storage/sqlite/sqlite.go:223-225\n\nProblem:\n- Under concurrent write load, BEGIN IMMEDIATE can fail with SQLITE_BUSY\n- Current implementation fails immediately instead of retrying\n- Results in spurious failures under normal concurrent usage\n\nSolution: Implement exponential backoff retry:\n- Retry up to N times (e.g., 5)\n- Backoff: 10ms, 20ms, 40ms, 80ms, 160ms\n- Check for context cancellation between retries\n- Only retry on SQLITE_BUSY/database locked errors\n\nImpact: Spurious failures under concurrent write load\n\nEffort: 3 hours","status":"open","priority":1,"issue_type":"feature","created_at":"2025-11-16T14:51:31.247147-08:00","updated_at":"2025-11-16T14:51:31.247147-08:00"} {"id":"bd-ork0","title":"Add comments to 30+ silently ignored errors or fix them","description":"Code health review found 30+ instances of error suppression using blank identifier without explanation:\n\nGood examples (with comments):\n- merge.go: _ = gitRmCmd.Run() // Ignore errors\n- daemon_watcher.go: _ = watcher.Add(...) // Ignore error\n\nBad examples (no context):\n- create.go:213: dbPrefix, _ = store.GetConfig(ctx, \"issue_prefix\")\n- daemon_sync_branch.go: _ = daemonClient.Close()\n- migrate_hash_ids.go, version_tracking.go: _ = store.Close()\n\nFix: Add comments explaining WHY errors are ignored, or handle them properly.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-16T18:17:25.899372-08:00","updated_at":"2025-12-16T18:17:25.899372-08:00","dependencies":[{"issue_id":"bd-ork0","depends_on_id":"bd-tggf","type":"blocks","created_at":"2025-12-16T18:19:06.275843-08:00","created_by":"daemon","metadata":"{}"}]} {"id":"bd-otf4","title":"Code Review: PR #481 - Context Engineering Optimizations","description":"Comprehensive code review of the merged context engineering PR (PR #481) that reduces MCP context usage by 80-90%.\n\n## Summary\nThe PR successfully implements lazy tool schema loading and minimal issue models to reduce context window usage. Overall implementation is solid and well-tested.\n\n## Positive Findings\n✅ Well-designed models (IssueMinimal, CompactedResult)\n✅ Comprehensive test coverage (28 tests, all passing)\n✅ Clear documentation and comments\n✅ Backward compatibility preserved (show() still returns full Issue)\n✅ Sensible defaults (COMPACTION_THRESHOLD=20, PREVIEW_COUNT=5)\n✅ Tool catalog complete with all 15 tools documented\n\n## Issues Identified\nSee linked issues for specific followup tasks.\n\n## Context Engineering Architecture\n- discover_tools(): List tool names only (~500 bytes vs ~15KB)\n- get_tool_info(name): Get specific tool details on-demand\n- IssueMinimal: Lightweight model for list views (~80 bytes vs ~400 bytes)\n- CompactedResult: Auto-compacts results with \u003e20 issues\n- _to_minimal(): Conversion function (efficient, no N+1 issues)","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-14T14:24:13.523532-08:00","updated_at":"2025-12-14T14:24:13.523532-08:00"} +{"id":"bd-otli","title":"Wait for CI to pass","description":"Monitor GitHub Actions - all checks must pass before release artifacts are built","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:03.022281-08:00","updated_at":"2025-12-19T22:56:03.022281-08:00","dependencies":[{"issue_id":"bd-otli","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:15.097564-08:00","created_by":"daemon"},{"issue_id":"bd-otli","depends_on_id":"bd-7tuu","type":"blocks","created_at":"2025-12-19T22:56:23.360436-08:00","created_by":"daemon"}]} {"id":"bd-pbh","title":"Release v0.30.4","description":"## Version Bump Workflow\n\nCoordinating release from 0.30.3 to 0.30.4.\n\n### Components Updated\n- Go CLI (cmd/bd/version.go)\n- Claude Plugin (.claude-plugin/*.json)\n- MCP Server (integrations/beads-mcp/)\n- npm Package (npm-package/package.json)\n- Git hooks (cmd/bd/templates/hooks/)\n\n### Release Channels\n- GitHub Releases (GoReleaser)\n- PyPI (beads-mcp)\n- npm (@beads/cli)\n- Homebrew (homebrew-beads tap)\n","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-17T21:19:10.926133-08:00","updated_at":"2025-12-17T21:46:46.192948-08:00","closed_at":"2025-12-17T21:46:46.192948-08:00","labels":["release","v0.30.4","workflow"]} {"id":"bd-pbh.1","title":"Update cmd/bd/version.go to 0.30.4","description":"Update the Version constant in cmd/bd/version.go:\n```go\nVersion = \"0.30.4\"\n```\n\n\n```verify\ngrep -q 'Version = \"0.30.4\"' cmd/bd/version.go\n```","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-17T21:19:10.9462-08:00","updated_at":"2025-12-17T21:46:46.20387-08:00","closed_at":"2025-12-17T21:46:46.20387-08:00","labels":["workflow"],"dependencies":[{"issue_id":"bd-pbh.1","depends_on_id":"bd-pbh","type":"parent-child","created_at":"2025-12-17T21:19:10.946633-08:00","created_by":"daemon","metadata":"{}"}]} {"id":"bd-pbh.10","title":"Run check-versions.sh - all must pass","description":"Run the version consistency check:\n```bash\n./scripts/check-versions.sh\n```\n\nAll versions must match 0.30.4.\n\n\n```verify\n./scripts/check-versions.sh\n```","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-17T21:19:11.047311-08:00","updated_at":"2025-12-17T21:46:46.28316-08:00","closed_at":"2025-12-17T21:46:46.28316-08:00","labels":["workflow"],"dependencies":[{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh","type":"parent-child","created_at":"2025-12-17T21:19:11.047888-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.1","type":"blocks","created_at":"2025-12-17T21:19:11.159084-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.4","type":"blocks","created_at":"2025-12-17T21:19:11.168248-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.5","type":"blocks","created_at":"2025-12-17T21:19:11.177869-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.6","type":"blocks","created_at":"2025-12-17T21:19:11.187629-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.7","type":"blocks","created_at":"2025-12-17T21:19:11.199955-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.8","type":"blocks","created_at":"2025-12-17T21:19:11.211479-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-pbh.10","depends_on_id":"bd-pbh.9","type":"blocks","created_at":"2025-12-17T21:19:11.224059-08:00","created_by":"daemon","metadata":"{}"}]} @@ -246,6 +247,7 @@ {"id":"bd-s0qf","title":"GH#405: Fix prefix parsing with hyphens - multi-hyphen prefixes parsed incorrectly","description":"Fixed: ExtractIssuePrefix was falling back to first-hyphen for word-like suffixes, breaking multi-hyphen prefixes like 'hacker-news' and 'me-py-toolkit'.","status":"tombstone","priority":2,"issue_type":"bug","created_at":"2025-12-16T01:13:56.951359-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-s2t","title":"wish: a 'continue' or similar cmd/flag which means alter last issue","description":"so many time I create an issue and then have another thought: 'oh, before I did X and it crashed there was ZZZ happening' or 'actually that is P4 not P2'. It would be nice if when `bd {cmd}` is used without a {title} or {id} it just adds or updates the most recently touched issue.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-08T06:46:37.529160416-07:00","updated_at":"2025-12-08T06:46:37.529160416-07:00"} {"id":"bd-sh4c","title":"Improve test coverage for cmd/bd/setup (28.4% → 50%)","description":"The setup package has only 28.4% test coverage. Setup commands are critical for first-time user experience.\n\nCurrent coverage: 28.4%\nTarget coverage: 50%","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T20:43:04.409346-08:00","updated_at":"2025-12-13T21:01:18.98833-08:00"} +{"id":"bd-si4g","title":"Verify release artifacts","description":"Check GitHub releases page - binaries for darwin/linux/windows should be available","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:04.183029-08:00","updated_at":"2025-12-19T22:56:04.183029-08:00","dependencies":[{"issue_id":"bd-si4g","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:15.173619-08:00","created_by":"daemon"},{"issue_id":"bd-si4g","depends_on_id":"bd-otli","type":"blocks","created_at":"2025-12-19T22:56:23.428507-08:00","created_by":"daemon"}]} {"id":"bd-siz1","title":"GH#532: bd sync circular error (suggests running bd sync)","description":"bd sync error message recommends running bd sync to fix the bd sync error. Fix error handling to provide useful guidance. See GitHub issue #532.","status":"tombstone","priority":2,"issue_type":"bug","created_at":"2025-12-16T01:04:00.543573-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-su45","title":"Protect pinned issues from bd cleanup/compact","description":"Update bd cleanup and bd compact to never delete pinned issues, even if they are closed. Pinned issues should persist indefinitely as reference material.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T23:33:46.204783-08:00","updated_at":"2025-12-19T17:43:35.712617-08:00","closed_at":"2025-12-19T00:43:04.06406-08:00","dependencies":[{"issue_id":"bd-su45","depends_on_id":"bd-0vg","type":"blocks","created_at":"2025-12-18T23:33:56.64582-08:00","created_by":"daemon"},{"issue_id":"bd-su45","depends_on_id":"bd-7h5","type":"blocks","created_at":"2025-12-18T23:34:07.857586-08:00","created_by":"daemon"}]} {"id":"bd-svb5","title":"GH#505: Add bd reset/wipe command","description":"Add command to cleanly reset/wipe beads database. User reports painful manual process to start fresh. See GitHub issue #505.","status":"tombstone","priority":2,"issue_type":"feature","created_at":"2025-12-16T01:03:42.160966-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"feature"} diff --git a/cmd/bd/autoflush.go b/cmd/bd/autoflush.go index d5e28bb6..8a839477 100644 --- a/cmd/bd/autoflush.go +++ b/cmd/bd/autoflush.go @@ -165,6 +165,7 @@ func autoImportIfNewer() { fmt.Fprintf(os.Stderr, "Auto-import skipped: parse error at line %d: %v\nSnippet: %s\n", lineNo, err, snippet) return } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) // Fix closed_at invariant: closed issues must have closed_at timestamp if issue.Status == types.StatusClosed && issue.ClosedAt == nil { @@ -662,6 +663,7 @@ func flushToJSONLWithState(state flushState) { } var issue types.Issue if err := json.Unmarshal([]byte(line), &issue); err == nil { + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) issueMap[issue.ID] = &issue } else { // Warn about malformed JSONL lines diff --git a/cmd/bd/autoimport.go b/cmd/bd/autoimport.go index 00b0d443..dc800e42 100644 --- a/cmd/bd/autoimport.go +++ b/cmd/bd/autoimport.go @@ -294,6 +294,7 @@ func importFromGit(ctx context.Context, dbFilePath string, store storage.Storage if err := json.Unmarshal([]byte(line), &issue); err != nil { return fmt.Errorf("failed to parse issue: %w", err) } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) issues = append(issues, &issue) } diff --git a/cmd/bd/compact.go b/cmd/bd/compact.go index 8caf92a6..21c05342 100644 --- a/cmd/bd/compact.go +++ b/cmd/bd/compact.go @@ -953,6 +953,7 @@ func pruneExpiredTombstones(customTTL time.Duration) (*TombstonePruneResult, err // Skip corrupt lines continue } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) allIssues = append(allIssues, &issue) } if err := file.Close(); err != nil { @@ -1049,6 +1050,7 @@ func previewPruneTombstones(customTTL time.Duration) (*TombstonePruneResult, err // Skip corrupt lines continue } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) allIssues = append(allIssues, &issue) } diff --git a/cmd/bd/daemon_sync.go b/cmd/bd/daemon_sync.go index 536673a2..f5c61ebf 100644 --- a/cmd/bd/daemon_sync.go +++ b/cmd/bd/daemon_sync.go @@ -183,6 +183,7 @@ func importToJSONLWithStore(ctx context.Context, store storage.Storage, jsonlPat fmt.Fprintf(os.Stderr, "Warning: failed to parse JSONL line %d: %v\n", lineNum, err) continue } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) issues = append(issues, &issue) } diff --git a/cmd/bd/import.go b/cmd/bd/import.go index ddacfc61..c8f46fc2 100644 --- a/cmd/bd/import.go +++ b/cmd/bd/import.go @@ -205,6 +205,7 @@ NOTE: Import requires direct database access and does not work with daemon mode. fmt.Fprintf(os.Stderr, "Error parsing line %d: %v\n", lineNum, err) os.Exit(1) } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) allIssues = append(allIssues, &issue) } diff --git a/cmd/bd/nodb.go b/cmd/bd/nodb.go index b0118d65..6196e9eb 100644 --- a/cmd/bd/nodb.go +++ b/cmd/bd/nodb.go @@ -103,6 +103,7 @@ func loadIssuesFromJSONL(path string) ([]*types.Issue, error) { if err := json.Unmarshal([]byte(line), &issue); err != nil { return nil, fmt.Errorf("line %d: %w", lineNum, err) } + issue.SetDefaults() // Apply defaults for omitted fields (beads-399) issues = append(issues, &issue) } diff --git a/internal/types/types.go b/internal/types/types.go index 7e32e2a4..f2d4559b 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -12,13 +12,13 @@ type Issue struct { ID string `json:"id"` ContentHash string `json:"-"` // Internal: SHA256 hash of canonical content (excludes ID, timestamps) - NOT exported to JSONL Title string `json:"title"` - Description string `json:"description"` + Description string `json:"description,omitempty"` 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"` + Status Status `json:"status,omitempty"` + Priority int `json:"priority,omitempty"` + IssueType IssueType `json:"issue_type,omitempty"` Assignee string `json:"assignee,omitempty"` EstimatedMinutes *int `json:"estimated_minutes,omitempty"` CreatedAt time.Time `json:"created_at"` @@ -190,6 +190,27 @@ func (i *Issue) ValidateWithCustomStatuses(customStatuses []string) error { return nil } +// SetDefaults applies default values for fields omitted during JSONL import. +// Call this after json.Unmarshal to ensure missing fields have proper defaults: +// - Status: defaults to StatusOpen if empty +// - Priority: defaults to 2 if zero (note: P0 issues must explicitly set priority=0) +// - IssueType: defaults to TypeTask if empty +// +// This enables smaller JSONL output by using omitempty on these fields. +func (i *Issue) SetDefaults() { + if i.Status == "" { + i.Status = StatusOpen + } + // Note: priority 0 (P0) is a valid value, so we can't distinguish between + // "explicitly set to 0" and "omitted". For JSONL compactness, we treat + // priority 0 in JSONL as P0, not as "use default". This is the expected + // behavior since P0 issues are explicitly marked. + // Priority default of 2 only applies to new issues via Create, not import. + if i.IssueType == "" { + i.IssueType = TypeTask + } +} + // Status represents the current state of an issue type Status string diff --git a/internal/types/types_test.go b/internal/types/types_test.go index 4ca2f54d..0b93e77f 100644 --- a/internal/types/types_test.go +++ b/internal/types/types_test.go @@ -900,3 +900,62 @@ func containsMiddle(s, substr string) bool { } return false } + +func TestSetDefaults(t *testing.T) { + tests := []struct { + name string + issue Issue + expectedStatus Status + expectedType IssueType + }{ + { + name: "empty fields get defaults", + issue: Issue{Title: "Test"}, + expectedStatus: StatusOpen, + expectedType: TypeTask, + }, + { + name: "existing status preserved", + issue: Issue{ + Title: "Test", + Status: StatusInProgress, + }, + expectedStatus: StatusInProgress, + expectedType: TypeTask, + }, + { + name: "existing type preserved", + issue: Issue{ + Title: "Test", + IssueType: TypeBug, + }, + expectedStatus: StatusOpen, + expectedType: TypeBug, + }, + { + name: "all fields set - no changes", + issue: Issue{ + Title: "Test", + Status: StatusClosed, + IssueType: TypeFeature, + ClosedAt: timePtr(time.Now()), + }, + expectedStatus: StatusClosed, + expectedType: TypeFeature, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + issue := tt.issue + issue.SetDefaults() + + if issue.Status != tt.expectedStatus { + t.Errorf("SetDefaults() Status = %v, want %v", issue.Status, tt.expectedStatus) + } + if issue.IssueType != tt.expectedType { + t.Errorf("SetDefaults() IssueType = %v, want %v", issue.IssueType, tt.expectedType) + } + }) + } +}