Commit Graph

29 Commits

Author SHA1 Message Date
beads/crew/emma
576a517c59 feat(multirepo): trust non-built-in types during hydration (bd-9ji4z)
Implements federation trust model for multi-repo type validation:
- Built-in types are validated (catch typos)
- Non-built-in types trusted from source repos

Changes:
- Add IssueType.IsBuiltIn() method
- Add Issue.ValidateForImport() for trust-based validation
- Update upsertIssueInTx to use ValidateForImport

Closes: bd-dqwuf, bd-alpw2 | Epic: bd-9ji4z

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 14:12:48 -08:00
beads/crew/dave
b7358f17bf feat(types): add custom type support to beads config (bd-649s)
Add types.custom config key mirroring the status.custom pattern:
- Add CustomTypeConfigKey constant and GetCustomTypes() to storage interface
- Add IssueType.IsValidWithCustom() method for validation
- Add ValidateWithCustom() to Issue for combined status/type validation
- Update all validation call sites to use GetCustomTypes()
- Rename parseCustomStatuses to parseCommaSeparated for reuse

This enables Gas Town to register custom types like agent/role/rig/convoy
without hardcoding them in beads core, supporting the type extraction epic.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Executed-By: beads/crew/dave
Rig: beads
Role: crew
2026-01-06 21:30:58 -08:00
Steve Yegge
1611f16751 refactor: remove unused bd pin/unpin/hook commands (bd-x0zl)
Analysis found these commands are dead code:
- gt never calls `bd pin` - uses `bd update --status=pinned` instead
- Beads.Pin() wrapper exists but is never called
- bd hook functionality duplicated by gt mol status
- Code comment says "pinned field is cosmetic for bd hook visibility"

Removed:
- cmd/bd/pin.go
- cmd/bd/unpin.go
- cmd/bd/hook.go

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 16:02:15 -08:00
Steve Yegge
c8b912cbe6 bd sync: 2025-12-27 15:56:42 2025-12-27 15:56:42 -08:00
Steve Yegge
ea8ae11002 feat: Rename 'wisp' to 'ephemeral' in beads API (bd-o18s)
BREAKING CHANGE: API field and CLI command renamed

- types.Issue.Wisp → types.Issue.Ephemeral
- JSON field: "wisp" → "ephemeral"
- CLI: bd wisp → bd ephemeral
- Flags: --wisp → --ephemeral
- ID prefix: wisp → eph

The SQLite column already uses 'ephemeral' so no schema migration needed.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 21:07:37 -08:00
Steve Yegge
9dc0da9fdd docs: Document COALESCE/NULLIF clone-local field protection pattern
Expands the inline comment to serve as a reference for when to use the
COALESCE(NULLIF(?, zero), column) defensive idiom in JSONL imports.

Pattern protects clone-local state (pinned, gate fields) from being
overwritten when importing JSONL where omitempty causes zero values to
represent "field absent" rather than "field is zero".

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 00:45:59 -08:00
Steve Yegge
7fbc2766e2 fix(multirepo): Preserve gate await fields during upsert (bd-gr4q)
Apply COALESCE(NULLIF(...)) pattern to await_type, await_id, timeout_ns,
and waiters fields in upsertIssueInTx. This prevents gate await fields
from being cleared when importing issues from JSONL that don't have
these fields (since gates are wisps and aren't exported to JSONL.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)
2025-12-25 23:21:03 -08:00
Steve Yegge
e67f27c092 bd sync: 2025-12-23 23:38:57 2025-12-24 00:06:41 -08:00
Steve Yegge
2de1695615 bd sync: 2025-12-23 22:33:32 2025-12-23 22:33:33 -08:00
Steve Yegge
cf9e5a597b bd sync: 2025-12-23 20:50:50 2025-12-23 20:50:50 -08:00
Steve Yegge
9c8761abc9 bd sync: 2025-12-23 20:45:19 2025-12-23 20:45:19 -08:00
Steve Yegge
0d31e4209d fix: preserve pinned field during JSONL import (bd-phtv)
Fixed two code paths where pinned=false from JSONL would overwrite
existing pinned=true in database:
- importer.go: Only update pinned if explicitly true in JSONL
- multirepo.go: Use COALESCE to preserve existing pinned value

Added tests for pinned field preservation.

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

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 13:49:22 -08:00
Steve Yegge
47b86b35d8 feat: add gate issue type and CLI commands for async coordination (bd-udsi)
Add async gates - coordination primitives for agents to wait on external events
like CI completion, PR merges, timers, or human approval.

Changes:
- Add 'gate' issue type to types.go with gate-specific fields:
  - AwaitType: condition type (gh:run, gh:pr, timer, human, mail)
  - AwaitID: condition identifier
  - Timeout: max wait duration
  - Waiters: mail addresses to notify when gate clears
- Add SQLite migration 027_gate_columns for new fields
- Update all SQLite storage queries to handle gate fields
- Add bd gate commands: create, show, list, close, wait
- All commands support --json output and --no-daemon mode

Closes: bd-2v0f, bd-lz49, bd-u66e

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 12:06:42 -08:00
Charles P. Cross
e360a74b61 fix: resolve all golangci-lint errors (#699)
Fix 12 linting issues that were causing CI failures:

errcheck (8 fixes):
- cmd/bd/mol_burn.go: wrap wispStore.Close() in defer func
- cmd/bd/mol_burn.go: handle fmt.Scanln() return value
- cmd/bd/mol_squash.go: wrap wispStore.Close() in defer func (2 locations)
- cmd/bd/wisp.go: wrap wispStore.Close() in defer func (3 locations)
- internal/beads/beads.go: wrap mainStore.Close() and wispStore.Close()

gosec G104 (2 fixes):
- internal/storage/sqlite/multirepo.go: handle rows.Close() errors

misspell (1 fix):
- cmd/bd/mol_burn.go: change "Cancelled" to "Canceled"

unparam (1 fix):
- cmd/bd/mol_squash.go: use blank identifier for unused cmd parameter

All fixes follow existing patterns in the codebase:
- defer func() { _ = store.Close() }() for deferred closes
- _ = rows.Close() for explicit closes with ignored errors
- _, _ = fmt.Scanln() for ignored return values

Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
2025-12-22 14:06:45 -08:00
Steve Yegge
8f8e9516df fix: bd repo commands write to YAML and cleanup on remove (#683)
- bd repo add/remove now writes to .beads/config.yaml instead of database
- bd repo remove deletes hydrated issues from the removed repo
- Added internal/config/repos.go for YAML config manipulation
- Added DeleteIssuesBySourceRepo for cleanup on remove

Fixes config disconnect where bd repo add wrote to DB but hydration read from YAML.

Breaking change: bd repo add no longer accepts optional alias argument.

Co-authored-by: Dylan Conlin <dylan.conlin@gmail.com>

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 01:26:45 -08:00
Steve Yegge
1cfb23487b feat: bd ready filters by external dep satisfaction (bd-zmmy)
GetReadyWork now lazily resolves external dependencies at query time:
- External refs (external:project:capability) checked against target DB
- Issues with unsatisfied external deps are filtered from ready list
- Satisfaction = closed issue with provides:<capability> label in target

Key changes:
- Remove FK constraint on depends_on_id to allow external refs
- Add migration 025 to drop FK and recreate views
- Filter external deps in GetReadyWork, not in blocked_issues_cache
- Add application-level validation for orphaned local deps
- Comprehensive tests for external dep resolution

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 23:41:44 -08:00
Steve Yegge
358d076fde refactor: rename Ephemeral → Wisp (Steam Engine metaphor)
Wisp = ephemeral vapor produced by the Steam Engine (Gas Town).
This aligns with the metaphor:
- Claude = Fire
- Claude Code = Steam
- Gas Town = Steam Engine
- Wisps = ephemeral vapor it produces

Changes:
- types.Issue.Ephemeral → types.Issue.Wisp
- types.IssueFilter.Ephemeral → types.IssueFilter.Wisp
- JSON field: "ephemeral" → "wisp"
- CLI flag: --ephemeral → --wisp (bd cleanup)
- All tests updated

Note: SQLite column remains "ephemeral" (no migration needed).
This is a breaking change for JSON consumers using 0.33.0.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 15:22:45 -08:00
Steve Yegge
52135d0370 feat(types): add template molecules infrastructure for beads-1ra
Add support for template molecules (is_template field and TypeMolecule type):

- Add IsTemplate field to Issue type with JSON support
- Add TypeMolecule constant to IssueType constants
- Add IsTemplate filter to IssueFilter for querying
- Update all SQL queries to include is_template column
- Add migration 024 for is_template column
- Add FindMoleculesJSONLInDir helper for molecules.jsonl path detection

This enables treating certain issues as read-only templates that can be
instantiated to create work items. The template flag allows separating
template molecules from regular work items in queries and exports.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 22:03:22 -08:00
Steve Yegge
b1ba1c5315 feat(storage): add pinned field to issues schema
Add pinned column to the issues table to support persistent context markers
that should not be treated as work items (bd-7h5).

Changes:
- Add pinned column to schema.go CREATE TABLE
- Add migration 023_pinned_column.go for existing databases
- Update all issue queries to include pinned column
- Update scanIssues and scanIssuesWithDependencyType to handle pinned field
- Add Pinned field to types.Issue struct with JSON serialization
- Fix migrations_test.go to include pinned in legacy schema test

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 00:08:32 -08:00
Steve Yegge
7c8b69f5b3 Phase 4: Remove deprecated edge fields from Issue struct (Decision 004)
This is the final phase of the Edge Schema Consolidation. It removes
the deprecated edge fields (RepliesTo, RelatesTo, DuplicateOf, SupersededBy)
from the Issue struct and all related code.

Changes:
- Remove edge fields from types.Issue struct
- Remove edge field scanning from queries.go and transaction.go
- Update graph_links_test.go to use dependency API exclusively
- Update relate.go to use AddDependency/RemoveDependency
- Update show.go with helper functions for thread traversal via deps
- Update mail_test.go to verify thread links via dependencies
- Add migration 022 to drop columns from issues table
- Fix cycle detection to allow bidirectional relates-to links
- Fix migration 022 to disable foreign keys before table recreation

All edge relationships now use the dependencies table exclusively.
The old Issue fields are fully removed.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 02:48:13 -08:00
Steve Yegge
0aea2d93c6 feat(schema): add messaging fields for bd-kwro epic
- Add TypeMessage issue type for inter-agent communication
- Add 6 new Issue fields: Sender, Ephemeral, RepliesTo, RelatesTo,
  DuplicateOf, SupersededBy
- Add 4 new dependency types: replies-to, relates-to, duplicates, supersedes
- Create migration 019_messaging_fields with indexes
- Update all CRUD operations across storage layer
- Fix reset_test.go to use correct function names
- Fix redundant newline lint error in sync.go

Closes: bd-kwro.1

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 13:06:47 -08:00
Steve Yegge
c7e119a1cc fix(import): defensive handling for closed issues missing closed_at timestamp
Fixes GH#523: older versions of bd could close issues without setting
the closed_at timestamp. When importing such issues, validation would
fail with "closed issues must have closed_at timestamp".

This fix adds defensive handling in all issue creation/validation paths:
- If status is "closed" and closed_at is nil, set closed_at to
  max(created_at, updated_at) + 1 second
- Similarly for tombstones missing deleted_at

Applied to:
- batch_ops.go: validateBatchIssuesWithCustomStatuses (main import path)
- transaction.go: CreateIssue and CreateIssues
- queries.go: CreateIssue
- multirepo.go: upsertIssueInTx

Also adds comprehensive tests for the defensive fix.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 17:21:44 -08:00
Steve Yegge
08e43d9fc7 feat(types): add tombstone support for inline soft-delete (bd-fbj)
Add tombstone types and schema migration as foundation for the tombstone
epic (bd-vw8) which replaces deletions.jsonl with inline tombstones.

Changes:
- Add tombstone fields to Issue struct: DeletedAt, DeletedBy, DeleteReason, OriginalType
- Add StatusTombstone constant and IsTombstone() helper method
- Update Status.IsValid() to accept tombstone status
- Create migration 018_tombstone_columns.go for new database columns
- Update schema.go with tombstone columns: deleted_at, deleted_by, delete_reason, original_type
- Update all issue insert/update/scan operations across:
  - issues.go (insertIssue, insertIssues)
  - queries.go (GetIssue, GetIssueByExternalRef, SearchIssues)
  - dependencies.go (scanIssues, scanIssuesWithDependencyType)
  - transaction.go (scanIssueRow, GetIssue, SearchIssues)
  - multirepo.go (import operations)
  - ready.go (GetReadyWork, GetStaleIssues)
  - labels.go (GetIssuesByLabel)
- Add test for IsTombstone() helper
- Update migration test to include tombstone columns

Unblocks: bd-olt (TTL logic), bd-3b4 (delete command), bd-0ih (merge updates), bd-dve (import/export)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:29:42 -08:00
Darin
e0734e230f fix: NixOS symlink compatibility for mtime and permission checks (#459)
* fix: use os.Lstat for symlink-safe mtime and permission checks

On NixOS and other systems using symlinks heavily (e.g., home-manager),
os.Stat follows symlinks and returns the target's metadata. This causes:

1. False staleness detection when JSONL is symlinked - mtime of target
   changes unpredictably when symlinks are recreated
2. os.Chmod failing or changing wrong file's permissions when target
   is in read-only location (e.g., /nix/store)
3. os.Chtimes modifying target's times instead of the symlink itself

Changes:
- autoimport.go: Use Lstat for JSONL mtime in CheckStaleness()
- import.go: Use Lstat in TouchDatabaseFile() for JSONL mtime
- export.go: Skip chmod for symlinked files
- multirepo.go: Use Lstat for JSONL mtime cache
- multirepo_export.go: Use Lstat for mtime, skip chmod for symlinks
- doctor/fix/permissions.go: Skip permission fixes for symlinked paths

These changes are safe cross-platform:
- On systems without symlinks, Lstat behaves identically to Stat
- Symlink permission bits are ignored on Unix anyway
- The extra Lstat syscall overhead is negligible

Fixes symlink-related data loss on NixOS. See GitHub issue #379.

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

Co-Authored-By: Claude <noreply@anthropic.com>

* test: add symlink behavior tests for NixOS compatibility

Add tests that verify symlink handling behavior:
- TestCheckStaleness_SymlinkedJSONL: verifies mtime detection uses
  symlink's own mtime (os.Lstat), not target's mtime (os.Stat)
- TestPermissions_SkipsSymlinkedBeadsDir: verifies chmod is skipped
  for symlinked .beads directories
- TestPermissions_SkipsSymlinkedDatabase: verifies chmod is skipped
  for symlinked database files while still fixing .beads dir perms

Also adds devShell to flake.nix for local development with go, gopls,
golangci-lint, and sqlite tools.

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

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 13:22:09 -08:00
Steve Yegge
09fffa4eaf fix(db): add close_reason column to issues table (bd-uyu)
- Add migration 017_close_reason_column.go to create the column
- Update all INSERT statements to include close_reason
- Update all SELECT statements to include close_reason
- Update doctor.go to check for close_reason in schema validation
- Remove workaround code that batch-loaded close reasons from events table
- Fix migrations_test.go to include close_reason in test table schema

This fixes sync loops where close_reason values were silently dropped
because the DB lacked the column despite the struct having the field.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 21:56:41 -08:00
Shaun Cutts
3a2e9d5852 fix(multirepo): handle out-of-order dependencies during JSONL import (#414)
* bd sync: 2025-11-29 00:08:58

* fix(multirepo): handle out-of-order dependencies during JSONL import

Fixes #413. When importing issues from multi-repo JSONL files, if issue A
(line 1) has a dependency on issue B (line 5), the import would fail with
FK constraint error because B doesn't exist yet.

Solution:
- Disable FK checks at start of importJSONLFile()
- Re-enable FK checks before commit
- Run PRAGMA foreign_key_check to validate data integrity
- Fail with clear error if orphaned dependencies are detected

This allows out-of-order dependencies while still catching corrupted data.

---------

Co-authored-by: Shaun Cutts <shauncutts@factfiber.com>
2025-11-29 22:07:52 -08:00
Steve Yegge
a5ec5c6977 feat(bd-1pj6): Add custom status states via config
Users can now define custom status states for multi-step pipelines using:
  bd config set status.custom "awaiting_review,awaiting_testing,awaiting_docs"

Changes:
- Add Status.IsValidWithCustom() method for custom status validation
- Add Issue.ValidateWithCustomStatuses() method
- Add GetCustomStatuses() method to storage interface
- Update CreateIssue/UpdateIssue to support custom statuses
- Add comprehensive tests for custom status functionality
- Update config command help text with custom status documentation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:19:21 -08:00
Steve Yegge
52cf2af9f7 Fix lint errors: add error check and gosec suppressions 2025-11-05 01:11:09 -08:00
Steve Yegge
05529fe4c0 Implement multi-repo hydration layer with mtime caching (bd-307)
- Add repo_mtimes table to track JSONL file modification times
- Implement HydrateFromMultiRepo() with mtime-based skip optimization
- Support tilde expansion for repo paths in config
- Add source_repo column via migration (not in base schema)
- Fix schema to allow migration on existing databases
- Comprehensive test coverage for hydration logic
- Resurrect missing parent issues bd-cb64c226 and bd-cbed9619

Implementation:
- internal/storage/sqlite/multirepo.go - Core hydration logic
- internal/storage/sqlite/multirepo_test.go - Test coverage
- docs/MULTI_REPO_HYDRATION.md - Documentation

Schema changes:
- source_repo column added via migration only (not base schema)
- repo_mtimes table for mtime caching
- All SELECT queries updated to include source_repo

Database recovery:
- Restored from 17 to 285 issues
- Created placeholder parents for orphaned hierarchical children

Amp-Thread-ID: https://ampcode.com/threads/T-faa1339a-14b2-426c-8e18-aa8be6f5cde6
Co-authored-by: Amp <amp@ampcode.com>
2025-11-04 23:12:41 -08:00