diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index fd9603e0..71beb2c8 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -17,7 +17,7 @@ {"id":"bd-0w5","title":"Fix update-hooks verification in version-bump.yaml","description":"The update-hooks task verification command at version-bump.yaml:358 always succeeds due to '|| echo ...' fallback. Remove the fallback so verification actually fails when hooks aren't installed.","status":"tombstone","priority":3,"issue_type":"bug","created_at":"2025-12-17T22:23:06.55467-08:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-0zp7","title":"Add missing hook calls in mail reply and ack","description":"The mail commands are missing hook calls:\n\n1. runMailReply (mail.go:525-672) creates a message but doesn't call hookRunner.Run(hooks.EventMessage, ...) after creating the reply in direct mode (around line 640)\n\n2. runMailAck (mail.go:432-523) closes messages but doesn't call hookRunner.Run(hooks.EventClose, ...) after closing each message (around line 487 for daemon mode, 493 for direct mode)\n\nThis means GGT hooks won't fire for replies or message acknowledgments.","status":"tombstone","priority":1,"issue_type":"bug","created_at":"2025-12-16T20:52:53.069412-08:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-118d","title":"Commit release v0.33.2","description":"Stage and commit the version bump:\n\n```bash\ngit add cmd/bd/version.go cmd/bd/info.go CHANGELOG.md\ngit commit -m \"release: v0.33.2\"\n```\n\nDo NOT push yet - tag first.","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-21T16:10:13.761725-08:00","updated_at":"2025-12-21T17:29:31.791368-08:00","deleted_at":"2025-12-21T17:29:31.791368-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} -{"id":"bd-11lm","title":"Increase ClaudeStartTimeout for slow startup machines","description":"ClaudeStartTimeout is 15s but Claude can take 30s+ to start on some machines. If Claude takes longer than timeout, GUPP nudges arrive before Claude is ready.\n\nOptions:\n1. Increase timeout to 60s (simple)\n2. Improve WaitForClaudeReady detection (harder - has false positives)\n3. Retry nudge if no response within N seconds\n\nLocation: internal/constants/constants.go","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-30T21:20:58.529924-08:00","created_by":"mayor","updated_at":"2025-12-30T21:20:58.529924-08:00"} +{"id":"bd-11lm","title":"Increase ClaudeStartTimeout for slow startup machines","description":"ClaudeStartTimeout is 15s but Claude can take 30s+ to start on some machines. If Claude takes longer than timeout, GUPP nudges arrive before Claude is ready.\n\nOptions:\n1. Increase timeout to 60s (simple)\n2. Improve WaitForClaudeReady detection (harder - has false positives)\n3. Retry nudge if no response within N seconds\n\nLocation: internal/constants/constants.go","status":"hooked","priority":2,"issue_type":"bug","assignee":"beads/polecats/pearl","created_at":"2025-12-30T21:20:58.529924-08:00","created_by":"mayor","updated_at":"2025-12-30T22:12:49.05538-08:00"} {"id":"bd-1200","title":"Merge: jasper-1767106250817","description":"branch: polecat/jasper-1767106250817\ntarget: main\nsource_issue: jasper-1767106250817\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-30T07:05:01.45534-08:00","created_by":"beads/polecats/jasper","updated_at":"2025-12-30T18:12:30.984243-08:00","closed_at":"2025-12-30T18:11:08.06873-08:00"} {"id":"bd-13c2","title":"Merge: garnet-1767138539231","description":"branch: polecat/garnet-1767138539231\ntarget: main\nsource_issue: garnet-1767138539231\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-30T16:00:34.708302-08:00","created_by":"beads/polecats/garnet","updated_at":"2025-12-30T18:12:30.975825-08:00","closed_at":"2025-12-30T18:11:07.955982-08:00"} {"id":"bd-14ie","title":"Work on beads-2vn: Add simple built-in beads viewer (GH#6...","description":"Work on beads-2vn: Add simple built-in beads viewer (GH#654). Add bd list --pretty with --watch flag, tree view with priority/status symbols. When done, submit MR (not PR) to integration branch for Refinery.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:56:47.305831-08:00","updated_at":"2025-12-19T23:28:32.429492-08:00","closed_at":"2025-12-19T23:23:13.928323-08:00"} @@ -74,6 +74,7 @@ {"id":"bd-2wm2","title":"bd repair: Check comments and events tables for orphans","description":"bd repair currently checks dependencies and labels tables, but the schema also has comments and events tables with issue_id foreign keys that could be orphaned.\n\nAdd orphan detection and cleanup for:\n- comments table (issue_id not in issues)\n- events table (issue_id not in issues)","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-12-29T12:48:11.563302-08:00","created_by":"stevey","updated_at":"2025-12-29T12:57:43.745176-08:00","closed_at":"2025-12-29T12:57:43.745176-08:00","close_reason":"Implemented in repair.go: added orphan detection for comments/events tables and --json output flag"} {"id":"bd-313v","title":"rpc: Rich mutation events not emitted","description":"The activity command (activity.go) references rich mutation event types (MutationBonded, MutationSquashed, MutationBurned, MutationStatus) that include metadata like OldStatus, NewStatus, ParentID, and StepCount.\n\nHowever, the emitMutation() function in server_core.go:141 only accepts (eventType, issueID) and only populates Type, IssueID, and Timestamp. The additional metadata fields are never set.\n\nNeed to either:\n1. Add an emitRichMutation() function that accepts the additional metadata\n2. Update call sites (close, bond, squash, burn operations) to emit rich events\n\nWithout this fix, the activity feed will never show:\n- Status transitions (in_progress -\u003e closed)\n- Bonded events with step counts\n- Parent molecule relationships\n\nDiscovered during code review of bd-xo1o implementation.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T04:06:17.39523-08:00","updated_at":"2025-12-23T04:13:19.205249-08:00","closed_at":"2025-12-23T04:13:19.205249-08:00"} {"id":"bd-31ae","title":"Document labels-as-state pattern for operational state","description":"Labels on role beads cache current operational state for fast queries.\n\nConvention: \u003cdimension\u003e:\u003cvalue\u003e\n- patrol:muted / patrol:active\n- mode:degraded / mode:normal\n- status:idle / status:working\n\nPattern:\n1. Create event bead (full context, immutable history)\n2. Update role bead labels (current state cache)\n\nNo schema change needed - labels already exist. This task:\n- Document the convention in beads docs\n- Consider helper: bd state \u003crole\u003e \u003cdimension\u003e (queries label)\n- Consider helper: bd set-state \u003crole\u003e \u003cdimension\u003e=\u003cvalue\u003e (creates event + updates label)\n\nEvents are source of truth. Labels are cache.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-30T15:15:46.697971-08:00","created_by":"gastown/crew/max","updated_at":"2025-12-30T18:12:30.961022-08:00","closed_at":"2025-12-30T15:51:34.364561-08:00","close_reason":"Added 'Labels as State Cache' section to docs/LABELS.md with convention, pattern, examples, and best practices"} +{"id":"bd-33ax","title":"Merge: granite-mjtmdru5","description":"branch: polecat/granite-mjtmdru5\ntarget: main\nsource_issue: granite-mjtmdru5\nrig: beads","status":"open","priority":2,"issue_type":"merge-request","created_at":"2025-12-30T22:14:26.689691-08:00","created_by":"beads/polecats/granite","updated_at":"2025-12-30T22:14:26.689691-08:00"} {"id":"bd-34q1","title":"Give --all flag actual meaning in bd list","description":"The --all flag exists but is currently a no-op ('flag provided for CLI familiarity'). Once we change the default to non-closed, --all should override to show all issues including closed.\n\nThis provides backwards compatibility for scripts that depend on seeing all issues.","notes":"## Implementation Plan\n\n### Code Changes (cmd/bd/list.go)\n\n1. **Update --all flag handling** (~line 889):\n```go\n// Currently: listCmd.Flags().Bool(\"all\", false, \"Show all issues (default behavior; flag provided for CLI familiarity)\")\n// Change to:\nlistCmd.Flags().Bool(\"all\", false, \"Show all issues including closed (overrides default filter)\")\n```\n\n2. **Check for --all in Run function** (before applying default filter):\n```go\nallFlag, _ := cmd.Flags().GetBool(\"all\")\nif status == \"\" \u0026\u0026 !allFlag {\n // Apply default non-closed filter\n filter.ExcludeStatus = []types.Status{\"closed\"}\n}\n// If --all is set, no ExcludeStatus applied = show everything\n```\n\n### Testing\n- `bd list --all` shows all issues including closed\n- `bd list` shows non-closed (from bd-mypl)\n- `bd list --all --status=open` - --all is ignored when explicit status given\n\n### Documentation\n- Update --help text\n- Add to migration guide: \"Use --all for previous default behavior\"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-29T15:25:07.522236-08:00","created_by":"stevey","updated_at":"2025-12-29T17:53:30.241263-08:00","closed_at":"2025-12-29T17:53:30.241263-08:00","close_reason":"Implemented in single commit","labels":["gh:788"],"dependencies":[{"issue_id":"bd-34q1","depends_on_id":"bd-mypl","type":"blocks","created_at":"2025-12-29T15:25:20.671576-08:00","created_by":"daemon"}]} {"id":"bd-379","title":"Implement `bd setup cursor` for Cursor IDE integration","description":"Create a `bd setup cursor` command that integrates Beads workflow into Cursor IDE via .cursorrules file. Unlike Claude Code (which has hooks), Cursor uses a static rules file to provide context to its AI.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-11-11T23:32:22.170083-08:00","updated_at":"2025-12-25T22:26:54.445182-08:00","closed_at":"2025-12-25T22:26:54.445182-08:00"} {"id":"bd-3852","title":"Add orphan detection migration","description":"Create migration to detect orphaned children in existing databases. Query: SELECT id FROM issues WHERE id LIKE '%.%' AND substr(id, 1, instr(id || '.', '.') - 1) NOT IN (SELECT id FROM issues). Log results, let user decide action (delete orphans or convert to top-level).","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-04T12:32:30.727044-08:00","updated_at":"2025-12-21T21:00:05.041582-08:00","closed_at":"2025-12-21T21:00:05.041582-08:00"} @@ -96,7 +97,7 @@ {"id":"bd-47qx","title":"bd cook: Proto ID prefix should match project prefix","description":"When `bd cook` creates a proto, it uses the formula name as the proto ID:\n\n```yaml\nformula: mol-deacon-patrol\n```\n\nCreates proto with ID `mol-deacon-patrol`.\n\n## Problem\n\nIf the project uses a different prefix (e.g., `gt-`), the cooked protos have \na different prefix (`mol-`), causing:\n\n1. `bd sync` import warnings about prefix mismatch\n2. Mixed prefix lists in `bd mol list`\n3. Confusion about naming conventions\n\n## Options\n\n1. **Use project prefix**: Cook as `gt-mol-deacon-patrol`\n2. **Allow mol- as special prefix**: Recognize mol-* as valid for protos\n3. **Make prefix configurable**: `bd cook --prefix gt-`\n4. **Keep current behavior**: Document that formulas use their own namespace\n\n## Current Workaround\n\nThe prefix mismatch warning can be ignored, but `bd sync` fails the import step.\n\n## Related\n\ngt-i6k1: Duplicate proto cleanup","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-24T13:50:24.631336-08:00","updated_at":"2025-12-24T13:59:09.933374-08:00","closed_at":"2025-12-24T13:59:09.933374-08:00"} {"id":"bd-47tn","title":"Add bd daemon --stop-all command to kill all daemon processes","description":"Currently there's no easy way to stop all running bd daemon processes. Users must resort to pkill -f 'bd daemon' or similar shell commands.\n\nAdd a --stop-all flag to bd daemon that:\n1. Finds all running bd daemon processes (not just the current repo's daemon)\n2. Gracefully stops them all\n3. Reports how many were stopped\n\nThis is useful when:\n- Multiple daemons are running and causing race conditions\n- User wants a clean slate before running bd sync\n- Debugging daemon-related issues","status":"tombstone","priority":2,"issue_type":"feature","created_at":"2025-12-13T06:34:45.080633-08:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"feature"} {"id":"bd-49kw","title":"Workaround for FastMCP outputSchema bug in Claude Code","description":"The beads MCP server (v0.23.1) successfully connects to Claude Code, but all tools fail to load with a schema validation error due to a bug in FastMCP 2.13.1.\n\nError: \"Invalid literal value, expected \\\"object\\\"\" in outputSchema.\n\nRoot Cause: FastMCP generates outputSchema with $ref at root level without \"type\": \"object\" for self-referential models (Issue).\n\nWorkaround: Use slash commands (/beads:ready) or wait for FastMCP fix.\n","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-20T18:55:39.041831-05:00","updated_at":"2025-12-23T23:49:44.371623-08:00","closed_at":"2025-12-23T23:49:44.371623-08:00"} -{"id":"bd-49oe","title":"Cache GetGitDir() for additional optimization","description":"Code review finding from bd-7di fix.\n\nGetGitDir() at line 17 is not cached but is called by:\n- GetGitHooksDir()\n- GetGitRefsDir()\n- GetGitHeadPath()\n\nIf any of those are called multiple times, we still spawn extra git processes. Consider adding sync.Once caching similar to the other functions.\n\nFile: internal/git/gitdir.go","status":"open","priority":4,"issue_type":"task","created_at":"2025-12-25T22:08:52.736529-08:00","updated_at":"2025-12-25T22:08:52.736529-08:00"} +{"id":"bd-49oe","title":"Cache GetGitDir() for additional optimization","description":"Code review finding from bd-7di fix.\n\nGetGitDir() at line 17 is not cached but is called by:\n- GetGitHooksDir()\n- GetGitRefsDir()\n- GetGitHeadPath()\n\nIf any of those are called multiple times, we still spawn extra git processes. Consider adding sync.Once caching similar to the other functions.\n\nFile: internal/git/gitdir.go","status":"closed","priority":4,"issue_type":"task","assignee":"beads/polecats/granite","created_at":"2025-12-25T22:08:52.736529-08:00","updated_at":"2025-12-30T22:14:16.659122-08:00","closed_at":"2025-12-30T22:14:16.659122-08:00","close_reason":"Already implemented. GetGitDir() uses getGitContext() which is cached via sync.Once. The gitContext struct (line 13-19) holds gitDir and is populated once by initGitContext(). All dependent functions (GetGitHooksDir, GetGitRefsDir, GetGitHeadPath) benefit from this caching. No changes needed."} {"id":"bd-4bsb","title":"Code review findings: mol squash deletion bypasses tombstones","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-21T13:57:14.154316-08:00","updated_at":"2025-12-21T18:01:06.811216-08:00","closed_at":"2025-12-21T18:01:06.811216-08:00","dependencies":[{"issue_id":"bd-4bsb","depends_on_id":"bd-2vh3.3","type":"discovered-from","created_at":"2025-12-21T13:57:14.155488-08:00","created_by":"daemon"}]} {"id":"bd-4d9d","title":"Test epic for duplicate swarm","status":"closed","priority":2,"issue_type":"epic","created_at":"2025-12-28T21:56:57.256997-08:00","created_by":"beads/crew/dave","updated_at":"2025-12-29T13:40:29.716788-08:00","closed_at":"2025-12-29T13:40:29.716788-08:00","close_reason":"Stale/spurious - test artifacts, merged PRs, or auto-close candidates"} {"id":"bd-4ec8","title":"Widespread double JSON encoding bug in daemon mode RPC calls","description":"Multiple CLI commands had the same double JSON encoding bug found in bd-1048. All commands that called ResolveID via RPC used string(resp.Data) instead of properly unmarshaling the JSON response. This caused IDs to retain JSON quotes (\"bd-1048\" instead of bd-1048), which then got double-encoded when passed to subsequent RPC calls.\n\nAffected commands:\n- bd show (3 instances)\n- bd dep add/remove/tree (5 instances)\n- bd label add/remove/list (3 instances)\n- bd reopen (1 instance)\n\nRoot cause: resp.Data is json.RawMessage (already JSON-encoded), so string() conversion preserves quotes.\n\nFix: Replace all string(resp.Data) with json.Unmarshal(resp.Data, \u0026id) for proper deserialization.\n\nAll commands now tested and working correctly with daemon mode.","status":"tombstone","priority":0,"issue_type":"bug","created_at":"2025-11-02T22:33:01.632691-08:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} @@ -197,7 +198,7 @@ {"id":"bd-89f89fc0","title":"Remove unreachable RPC methods","description":"Several RPC server and client methods are unreachable and should be removed:\n\nServer methods (internal/rpc/server.go):\n- `Server.GetLastImportTime` (line 2116)\n- `Server.SetLastImportTime` (line 2123)\n- `Server.findJSONLPath` (line 2255)\n\nClient methods (internal/rpc/client.go):\n- `Client.Import` (line 311) - RPC import not used (daemon uses autoimport)\n\nEvidence:\n```bash\ngo run golang.org/x/tools/cmd/deadcode@latest -test ./...\n```\n\nImpact: Removes ~80 LOC of unused RPC code","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.432202-07:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} {"id":"bd-8b0x","title":"Remove molecule.go (simple instantiation)","description":"molecule.go uses is_template field for simple single-issue cloning. This is too simple for what molecules should be - full DAG orchestration. The use case is covered by bd mol bond with a single-issue molecule. Delete molecule.go and its commands.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T23:52:15.041776-08:00","updated_at":"2025-12-21T00:04:32.335849-08:00","closed_at":"2025-12-21T00:04:32.335849-08:00","dependencies":[{"issue_id":"bd-8b0x","depends_on_id":"bd-ffjt","type":"blocks","created_at":"2025-12-20T23:52:25.807967-08:00","created_by":"daemon"}]} {"id":"bd-8ca7","title":"Merge: bd-au0.6","description":"branch: polecat/furiosa\ntarget: main\nsource_issue: bd-au0.6\nrig: beads","status":"closed","priority":1,"issue_type":"merge-request","created_at":"2025-12-23T20:42:30.870178-08:00","updated_at":"2025-12-23T21:21:57.695179-08:00","closed_at":"2025-12-23T21:21:57.695179-08:00"} -{"id":"bd-8cns","title":"gt sling --create fails to create polecat before resolving pane","description":"gt sling --create tries to resolve pane BEFORE creating the polecat, causing failure.\n\nExpected: gt sling gt-xyz gastown/newcat --create should:\n1. Create polecat if not exists\n2. Start session\n3. Hook work\n4. GUPP triggers autonomous execution\n\nActual: Fails with 'getting pane for gt-gastown-newcat: exit status 1'\n\nLocation: internal/cmd/sling.go - need to reorder operations","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-30T21:20:59.58382-08:00","created_by":"mayor","updated_at":"2025-12-30T21:20:59.58382-08:00"} +{"id":"bd-8cns","title":"gt sling --create fails to create polecat before resolving pane","description":"gt sling --create tries to resolve pane BEFORE creating the polecat, causing failure.\n\nExpected: gt sling gt-xyz gastown/newcat --create should:\n1. Create polecat if not exists\n2. Start session\n3. Hook work\n4. GUPP triggers autonomous execution\n\nActual: Fails with 'getting pane for gt-gastown-newcat: exit status 1'\n\nLocation: internal/cmd/sling.go - need to reorder operations","status":"hooked","priority":2,"issue_type":"bug","assignee":"beads/polecats/flint","created_at":"2025-12-30T21:20:59.58382-08:00","created_by":"mayor","updated_at":"2025-12-30T22:13:05.670667-08:00"} {"id":"bd-8e0q","title":"Merge: beads-ocs","description":"branch: polecat/valkyrie\ntarget: main\nsource_issue: beads-ocs\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T23:24:45.281478-08:00","updated_at":"2025-12-20T23:17:26.995706-08:00","closed_at":"2025-12-20T23:17:26.995706-08:00"} {"id":"bd-8fgn","title":"test hash length","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T13:49:32.113843-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-8fqd","title":"Test role bead","status":"closed","priority":2,"issue_type":"role","created_at":"2025-12-27T23:37:10.275712-08:00","created_by":"mayor","updated_at":"2025-12-27T23:37:15.585821-08:00","closed_at":"2025-12-27T23:37:15.585821-08:00"} @@ -501,7 +502,7 @@ {"id":"bd-ldb0","title":"Rename ephemeral → wisp throughout codebase","description":"## The Change\n\nRename 'ephemeral' to 'wisp' throughout the beads codebase.\n\n## Why\n\n**Ephemeral** is:\n- 4 syllables (too long)\n- Greek/academic (doesn't match bond/burn/squash)\n- Overused in tech (K8s, networking, storage)\n- Passive/descriptive\n\n**Wisp** is:\n- 1 syllable (matches bond/burn/squash)\n- Evocative - you can SEE a wisp\n- Steam engine metaphor - Gas Town is engines, steam wisps rise and dissipate\n- Will-o'-the-wisp - transient spirits that guide then vanish\n- Unique - nobody else uses it\n\n## The Steam Engine Metaphor\n\n```\nEngine does work → generates steam\nSteam wisps rise → execution trace\nSteam condenses → digest (distillate)\nSteam dissipates → cleaned up (burned)\n```\n\n## Full Vocabulary\n\n| Term | Meaning |\n|------|---------|\n| bond | Attach proto to work (creates wisps) |\n| wisp | Temporary execution step |\n| squash | Condense wisps into digest |\n| burn | Destroy wisps without record |\n| digest | Permanent condensed record |\n\n## Changes Required\n\n### Code\n- `Ephemeral bool` → `Wisp bool` in types/issue.go\n- `--ephemeral` flag → remove (wisp is default)\n- `--persistent` flag → keep as opt-out\n- `bd cleanup --ephemeral` → `bd cleanup --wisps`\n- Update all references in mol_*.go files\n\n### Docs\n- Update all documentation\n- Update CLAUDE.md examples\n- Update CLI help text\n\n### Database Migration\n- Add migration to rename field (or keep internal name, just change API)\n\n## Example Usage After\n\n```bash\nbd mol bond mol-polecat-work # Creates wisps (default)\nbd mol bond mol-xxx --persistent # Creates permanent issues\nbd mol squash bd-xxx # Condenses wisps → digest\nbd cleanup --wisps # Clean old wisps\nbd list --wisps # Show wisp issues\n```","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-21T14:44:41.576068-08:00","updated_at":"2025-12-22T00:32:31.153738-08:00","closed_at":"2025-12-22T00:32:31.153738-08:00"} {"id":"bd-lfak","title":"bd preflight: PR readiness checks for contributors","description":"## Vision\n\nEncode project-specific institutional knowledge into executable checks. CONTRIBUTING.md is documentation that's read once and forgotten; `bd preflight` is documentation that runs at exactly the right moment.\n\n## Problem Statement\n\nContributors face a \"last mile\" problem - they do the work but stumble on project-specific gotchas at PR time:\n- Nix vendorHash gets stale when go.sum changes\n- Beads artifacts leak into PRs (see bd-umbf for namespace solution)\n- Version mismatches between version.go and default.nix\n- Tests/lint not run locally before pushing\n- Other project-specific checks that only surface when CI fails\n\nThese are too obscure to remember, exist in docs nobody reads end-to-end, and waste CI round-trips.\n\n## Why beads?\n\nBeads already has a foothold in the contributor workflow. It knows:\n- Git state (staged files, branch, dirty status)\n- Project structure\n- The specific issue being worked on\n- Project-specific configuration\n\n## Proposed Interface\n\n### Tier 1: Checklist Mode (v1)\n\n $ bd preflight\n PR Readiness Checklist:\n\n [ ] Tests pass: go test -short ./...\n [ ] Lint passes: golangci-lint run ./...\n [ ] No beads pollution: check .beads/issues.jsonl diff\n [ ] Nix hash current: go.sum unchanged or vendorHash updated\n [ ] Version sync: version.go matches default.nix\n\n Run 'bd preflight --check' to validate automatically.\n\n### Tier 2: Check Mode (v2)\n\n $ bd preflight --check\n ✓ Tests pass\n ✓ Lint passes\n ⚠ Beads pollution: 3 issues in diff - are these project issues or personal?\n ✗ Nix hash stale: go.sum changed, vendorHash needs update\n Fix: sha256-KRR6dXzsSw8OmEHGBEVDBOoIgfoZ2p0541T9ayjGHlI=\n ✓ Version sync\n\n 1 error, 1 warning. Run 'bd preflight --fix' to auto-fix where possible.\n\n### Tier 3: Fix Mode (v3)\n\n $ bd preflight --fix\n ✓ Updated vendorHash in default.nix\n ⚠ Cannot auto-fix beads pollution - manual review needed\n\n## Checks to Implement\n\n| Check | Description | Auto-fixable |\n|-------|-------------|--------------|\n| tests | Run go test -short ./... | No |\n| lint | Run golangci-lint | Partial (gofmt) |\n| beads-pollution | Detect personal issues in diff | No (see bd-umbf) |\n| nix-hash | Detect stale vendorHash | Yes (if nix available) |\n| version-sync | version.go matches default.nix | Yes |\n| no-debug | No TODO/FIXME/console.log | Warn only |\n| clean-stage | No unintended files staged | Warn only |\n\n## Future: Configuration\n\nMake checks configurable per-project via .beads/preflight.yaml:\n\n preflight:\n checks:\n - name: tests\n run: go test -short ./...\n required: true\n - name: no-secrets\n pattern: \"**/*.env\"\n staged: deny\n - name: custom-check\n run: ./scripts/validate.sh\n\nThis lets any project using beads define their own preflight checks.\n\n## Implementation Phases\n\n### Phase 1: Static Checklist\n- Implement bd preflight with hardcoded checklist for beads\n- No execution, just prints what to check\n- Update CONTRIBUTING.md to reference it\n\n### Phase 2: Automated Checks\n- Implement bd preflight --check\n- Run tests, lint, detect stale hashes\n- Clear pass/fail/warn output\n\n### Phase 3: Auto-fix\n- Implement bd preflight --fix\n- Fix vendorHash, version sync\n- Integrate with bd-umbf solution for pollution\n\n### Phase 4: Configuration\n- .beads/preflight.yaml support\n- Make it useful for other projects using beads\n- Plugin/hook system for custom checks\n\n## Dependencies\n\n- bd-umbf: Namespace isolation for beads pollution (blocking for full solution)\n\n## Success Metrics\n\n- Fewer CI failures on first PR push\n- Reduced \"fix nix hash\" commits\n- Contributors report preflight caught issues before CI","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-13T18:01:39.587078-08:00","updated_at":"2025-12-13T18:01:39.587078-08:00","dependencies":[{"issue_id":"bd-lfak","depends_on_id":"bd-umbf","type":"blocks","created_at":"2025-12-13T18:01:46.059901-08:00","created_by":"daemon"}]} {"id":"bd-lfak.1","title":"Scaffold bd preflight command with checklist output","status":"closed","priority":2,"issue_type":"task","assignee":"beads/polecats/amber","created_at":"2025-12-30T18:06:29.297254-08:00","created_by":"mayor","updated_at":"2025-12-30T18:11:15.884294-08:00","closed_at":"2025-12-30T18:11:15.884294-08:00","close_reason":"Implemented static checklist output. Command now shows PR readiness checks. --check and --fix flags documented for future phases.","dependencies":[{"issue_id":"bd-lfak.1","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:29.297783-08:00","created_by":"mayor"}]} -{"id":"bd-lfak.2","title":"Implement --check flag with test runner","description":"## Summary\nImplement the `--check` flag to run `go test -short ./...` and report results.\n\n## Implementation Details\n\n### Location\nFile: `cmd/bd/preflight.go`\n\n### Changes Required\n\n1. **Add check execution logic** in `runPreflight()`:\n```go\nif check {\n runChecks(cmd) // New function\n return\n}\n```\n\n2. **Implement test runner**:\n```go\nfunc runTestCheck() CheckResult {\n cmd := exec.Command(\"go\", \"test\", \"-short\", \"./...\")\n output, err := cmd.CombinedOutput()\n return CheckResult{\n Name: \"tests\",\n Passed: err == nil,\n Output: string(output),\n Command: \"go test -short ./...\",\n }\n}\n```\n\n3. **Define CheckResult struct**:\n```go\ntype CheckResult struct {\n Name string\n Passed bool\n Output string\n Command string\n}\n```\n\n4. **Output format**:\n```\n$ bd preflight --check\n✓ Tests pass\n Command: go test -short ./...\n \n# On failure:\n✗ Tests fail\n Command: go test -short ./...\n Output: [truncated test output]\n```\n\n### Testing\n- Add tests in `cmd/bd/preflight_test.go`\n- Test with passing tests (this repo)\n- Test with intentionally failing test (mock or temp file)\n\n### Acceptance Criteria\n- [x] `--check` flag runs tests (not just prints message)\n- [x] Clear pass/fail output with ✓/✗ symbols\n- [x] Shows command being run\n- [x] Shows output on failure (truncated if long)\n- [x] Exits with non-zero code if tests fail\n- [x] Works with `--json` flag for programmatic use","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-30T18:06:30.419315-08:00","created_by":"mayor","updated_at":"2025-12-30T19:28:27.672048-08:00","dependencies":[{"issue_id":"bd-lfak.2","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:30.419875-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.2","depends_on_id":"bd-lfak.1","type":"blocks","created_at":"2025-12-30T18:06:48.582603-08:00","created_by":"mayor"}]} +{"id":"bd-lfak.2","title":"Implement --check flag with test runner","description":"## Summary\nImplement the `--check` flag to run `go test -short ./...` and report results.\n\n## Implementation Details\n\n### Location\nFile: `cmd/bd/preflight.go`\n\n### Changes Required\n\n1. **Add check execution logic** in `runPreflight()`:\n```go\nif check {\n runChecks(cmd) // New function\n return\n}\n```\n\n2. **Implement test runner**:\n```go\nfunc runTestCheck() CheckResult {\n cmd := exec.Command(\"go\", \"test\", \"-short\", \"./...\")\n output, err := cmd.CombinedOutput()\n return CheckResult{\n Name: \"tests\",\n Passed: err == nil,\n Output: string(output),\n Command: \"go test -short ./...\",\n }\n}\n```\n\n3. **Define CheckResult struct**:\n```go\ntype CheckResult struct {\n Name string\n Passed bool\n Output string\n Command string\n}\n```\n\n4. **Output format**:\n```\n$ bd preflight --check\n✓ Tests pass\n Command: go test -short ./...\n \n# On failure:\n✗ Tests fail\n Command: go test -short ./...\n Output: [truncated test output]\n```\n\n### Testing\n- Add tests in `cmd/bd/preflight_test.go`\n- Test with passing tests (this repo)\n- Test with intentionally failing test (mock or temp file)\n\n### Acceptance Criteria\n- [x] `--check` flag runs tests (not just prints message)\n- [x] Clear pass/fail output with ✓/✗ symbols\n- [x] Shows command being run\n- [x] Shows output on failure (truncated if long)\n- [x] Exits with non-zero code if tests fail\n- [x] Works with `--json` flag for programmatic use","status":"hooked","priority":2,"issue_type":"task","assignee":"beads/polecats/basalt","created_at":"2025-12-30T18:06:30.419315-08:00","created_by":"mayor","updated_at":"2025-12-30T22:13:39.341845-08:00","dependencies":[{"issue_id":"bd-lfak.2","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:30.419875-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.2","depends_on_id":"bd-lfak.1","type":"blocks","created_at":"2025-12-30T18:06:48.582603-08:00","created_by":"mayor"}]} {"id":"bd-lfak.3","title":"Implement lint check (golangci-lint)","description":"## Summary\nAdd lint check using golangci-lint to the preflight --check mode.\n\n## Implementation Details\n\n### Location\nFile: `cmd/bd/preflight.go`\n\n### Changes Required\n\n1. **Implement lint checker**:\n```go\nfunc runLintCheck() CheckResult {\n // First check if golangci-lint is available\n if _, err := exec.LookPath(\"golangci-lint\"); err != nil {\n return CheckResult{\n Name: \"lint\",\n Passed: false,\n Output: \"golangci-lint not found in PATH\",\n Command: \"golangci-lint run ./...\",\n Skipped: true,\n }\n }\n \n cmd := exec.Command(\"golangci-lint\", \"run\", \"./...\")\n output, err := cmd.CombinedOutput()\n return CheckResult{\n Name: \"lint\",\n Passed: err == nil,\n Output: string(output),\n Command: \"golangci-lint run ./...\",\n }\n}\n```\n\n2. **Add Skipped field to CheckResult** (if not already added by bd-lfak.2):\n```go\ntype CheckResult struct {\n Name string\n Passed bool\n Output string\n Command string\n Skipped bool // Tool not available\n}\n```\n\n3. **Output format**:\n```\n$ bd preflight --check\n✓ Lint passes\n Command: golangci-lint run ./...\n\n# When tool missing:\n⚠ Lint skipped (golangci-lint not found)\n Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest\n\n# On failure:\n✗ Lint fails\n Command: golangci-lint run ./...\n Output: [lint errors]\n```\n\n### Dependencies\n- Depends on bd-lfak.2 for CheckResult struct and runChecks() framework\n\n### Testing\n- Test with clean lint (this repo should pass)\n- Test with golangci-lint not in PATH (mock or temp PATH)\n- Test with lint errors (intentional bad code)\n\n### Notes\n- This repo has baseline warnings documented in docs/LINTING.md\n- golangci-lint respects .golangci.yml config if present\n\n### Acceptance Criteria\n- [x] Runs golangci-lint when --check flag is used\n- [x] Gracefully handles missing golangci-lint binary\n- [x] Shows clear pass/fail/skip output\n- [x] Shows lint errors on failure\n- [x] Works with --json flag","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-30T18:06:31.445431-08:00","created_by":"mayor","updated_at":"2025-12-30T19:28:51.93608-08:00","dependencies":[{"issue_id":"bd-lfak.3","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:31.447094-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.3","depends_on_id":"bd-lfak.1","type":"blocks","created_at":"2025-12-30T18:06:48.602793-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.3","depends_on_id":"bd-lfak.2","type":"blocks","created_at":"2025-12-30T19:31:30.950646-08:00","created_by":"beads/crew/emma"}]} {"id":"bd-lfak.4","title":"Implement nix-hash staleness detection","description":"## Summary\nDetect when go.sum has changed but vendorHash in default.nix may be stale.\n\n## Implementation Details\n\n### Location\nFile: `cmd/bd/preflight.go`\n\n### Detection Logic\n\n**Tier 1 (Simple - implement this):**\nCheck if go.sum has uncommitted changes (staged or unstaged). If so, warn that vendorHash might need updating.\n\n```go\nfunc runNixHashCheck() CheckResult {\n // Check if go.sum has changes\n cmd := exec.Command(\"git\", \"diff\", \"--name-only\", \"HEAD\", \"--\", \"go.sum\")\n output, _ := cmd.Output()\n \n // Also check staged changes\n stagedCmd := exec.Command(\"git\", \"diff\", \"--name-only\", \"--cached\", \"--\", \"go.sum\")\n stagedOutput, _ := stagedCmd.Output()\n \n hasChanges := len(output) \u003e 0 || len(stagedOutput) \u003e 0\n \n if hasChanges {\n return CheckResult{\n Name: \"nix-hash\",\n Passed: false,\n Output: \"go.sum has changes - vendorHash in default.nix may need updating\",\n Command: \"git diff HEAD -- go.sum\",\n Warning: true, // Not a hard failure, just a warning\n }\n }\n \n return CheckResult{\n Name: \"nix-hash\",\n Passed: true,\n Output: \"go.sum unchanged\",\n Command: \"git diff HEAD -- go.sum\",\n }\n}\n```\n\n**Tier 2 (Future - auto-fix):**\nIf nix is available, compute the correct vendorHash:\n```bash\nnix-prefetch-url --unpack \"https://...\"\n# or use nix build with --print-out-paths\n```\n\n### Key Files\n- `go.sum` - Go module checksums\n- `default.nix` - Contains vendorHash on line 12\n\n### Output Format\n```\n$ bd preflight --check\n✓ Nix hash current (go.sum unchanged)\n\n# When go.sum changed:\n⚠ Nix hash may be stale\n go.sum has uncommitted changes\n Hint: If you modified dependencies, run nix build to get new vendorHash\n \n# When nix available (future):\n✗ Nix hash stale\n Expected: sha256-KRR6dXzsSw8OmEHGBEVDBOoIgfoZ2p0541T9ayjGHlI=\n Current: sha256-ovG0EWQFtifHF5leEQTFvTjGvc+yiAjpAaqaV0OklgE=\n Fix: bd preflight --fix\n```\n\n### Add Warning field to CheckResult\n```go\ntype CheckResult struct {\n Name string\n Passed bool\n Output string\n Command string\n Skipped bool\n Warning bool // Yellow ⚠ instead of red ✗\n}\n```\n\n### Testing\n- Test with clean go.sum (no changes)\n- Test with modified go.sum (stage a fake change, then reset)\n- Test output format\n\n### Acceptance Criteria\n- [x] Detects when go.sum has uncommitted changes\n- [x] Shows as warning (⚠) not error (✗)\n- [x] Provides helpful hint about vendorHash\n- [x] Works with --json flag\n- [x] Does not require nix to be installed (just git)","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-30T18:06:32.318167-08:00","created_by":"mayor","updated_at":"2025-12-30T19:29:29.995126-08:00","dependencies":[{"issue_id":"bd-lfak.4","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:32.31998-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.4","depends_on_id":"bd-lfak.1","type":"blocks","created_at":"2025-12-30T18:06:48.621655-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.4","depends_on_id":"bd-lfak.2","type":"blocks","created_at":"2025-12-30T19:31:30.9704-08:00","created_by":"beads/crew/emma"}]} {"id":"bd-lfak.5","title":"Implement version-sync check (version.go vs default.nix)","description":"## Summary\nCheck that version in `cmd/bd/version.go` matches version in `default.nix`.\n\n## Current State (as of writing)\n- version.go: Version = \"0.41.0\"\n- default.nix: version = \"0.37.0\"\n- **These are already out of sync!** This check would catch this.\n\n## Implementation Details\n\n### Location\nFile: `cmd/bd/preflight.go`\n\n### Key Files\n- `cmd/bd/version.go` line 17: `Version = \"0.41.0\"`\n- `default.nix` line 4: `version = \"0.37.0\";`\n\n### Implementation\n\n```go\nfunc runVersionSyncCheck() CheckResult {\n // Read version.go\n versionGoContent, err := os.ReadFile(\"cmd/bd/version.go\")\n if err != nil {\n return CheckResult{\n Name: \"version-sync\",\n Passed: false,\n Output: fmt.Sprintf(\"Cannot read cmd/bd/version.go: %v\", err),\n Skipped: true,\n }\n }\n \n // Extract version from version.go\n // Pattern: Version = \"X.Y.Z\"\n versionGoRe := regexp.MustCompile(`Version\\s*=\\s*\"([^\"]+)\"`)\n versionGoMatch := versionGoRe.FindSubmatch(versionGoContent)\n if versionGoMatch == nil {\n return CheckResult{\n Name: \"version-sync\",\n Passed: false,\n Output: \"Cannot parse version from version.go\",\n Skipped: true,\n }\n }\n goVersion := string(versionGoMatch[1])\n \n // Read default.nix\n nixContent, err := os.ReadFile(\"default.nix\")\n if err != nil {\n return CheckResult{\n Name: \"version-sync\",\n Passed: true, // No nix file = no sync needed\n Output: \"default.nix not found (skipping nix version check)\",\n Skipped: true,\n }\n }\n \n // Extract version from default.nix\n // Pattern: version = \"X.Y.Z\";\n nixRe := regexp.MustCompile(`version\\s*=\\s*\"([^\"]+)\"`)\n nixMatch := nixRe.FindSubmatch(nixContent)\n if nixMatch == nil {\n return CheckResult{\n Name: \"version-sync\",\n Passed: false,\n Output: \"Cannot parse version from default.nix\",\n Skipped: true,\n }\n }\n nixVersion := string(nixMatch[1])\n \n if goVersion != nixVersion {\n return CheckResult{\n Name: \"version-sync\",\n Passed: false,\n Output: fmt.Sprintf(\"Version mismatch: version.go=%s, default.nix=%s\", goVersion, nixVersion),\n Command: \"Compare cmd/bd/version.go and default.nix\",\n }\n }\n \n return CheckResult{\n Name: \"version-sync\",\n Passed: true,\n Output: fmt.Sprintf(\"Versions match: %s\", goVersion),\n Command: \"Compare cmd/bd/version.go and default.nix\",\n }\n}\n```\n\n### Output Format\n```\n$ bd preflight --check\n✓ Version sync (0.41.0)\n\n# On mismatch:\n✗ Version mismatch\n version.go: 0.41.0\n default.nix: 0.37.0\n Fix: Update default.nix to match version.go\n```\n\n### Future: Auto-fix\nFor --fix mode, update default.nix automatically:\n```go\n// Replace version line in default.nix\nnewContent := nixRe.ReplaceAll(nixContent, \n []byte(fmt.Sprintf(`version = \"%s\"`, goVersion)))\nos.WriteFile(\"default.nix\", newContent, 0644)\n```\n\n### Testing\n- Test with matching versions\n- Test with mismatched versions (current state!)\n- Test with missing files\n\n### Acceptance Criteria\n- [x] Reads version from both files\n- [x] Compares and reports mismatch\n- [x] Shows both versions clearly on mismatch\n- [x] Works when default.nix is missing (skip gracefully)\n- [x] Works with --json flag","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-30T18:06:33.380378-08:00","created_by":"mayor","updated_at":"2025-12-30T19:30:02.607687-08:00","dependencies":[{"issue_id":"bd-lfak.5","depends_on_id":"bd-lfak","type":"parent-child","created_at":"2025-12-30T18:06:33.382134-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.5","depends_on_id":"bd-lfak.1","type":"blocks","created_at":"2025-12-30T18:06:48.64101-08:00","created_by":"mayor"},{"issue_id":"bd-lfak.5","depends_on_id":"bd-lfak.2","type":"blocks","created_at":"2025-12-30T19:31:30.989707-08:00","created_by":"beads/crew/emma"}]}