feat(init): auto-add 'landing the plane' instructions to AGENTS.md
Closes GH#516: bd init now automatically: - Creates AGENTS.md and @AGENTS.md with landing-the-plane instructions if they don't exist - Appends the instructions to existing files if they don't have them - Skips if the section already exists (idempotent) - Skips in stealth mode (user wants invisible setup) The landing-the-plane instructions ensure AI agents properly complete their work sessions by pushing all changes to remote before ending.
This commit is contained in:
@@ -148,7 +148,6 @@
|
||||
{"id":"bd-3tfh","title":"Benchmark Helper Functions","description":"Extend existing benchmark helpers in internal/storage/sqlite/bench_helpers_test.go (or create if organizing separately).\n\nExisting helper (in compact_bench_test.go):\n- setupBenchDB(tb) - Creates temp SQLite database with basic config\n * Used by compact and cycle benchmarks\n * Returns (*SQLiteStorage, cleanup func())\n\nNew helpers to add:\n- setupLargeBenchDB(b *testing.B) storage.Storage\n * Creates 10K issue database using LargeSQLite fixture\n * Returns configured storage instance\n \n- setupXLargeBenchDB(b *testing.B) storage.Storage\n * Creates 20K issue database using XLargeSQLite fixture\n * Returns configured storage instance\n\nImplementation options:\n1. Add to existing compact_bench_test.go (co-located with setupBenchDB)\n2. Create new bench_helpers_test.go for organization\n\nBoth approaches:\n- Build tag: //go:build bench\n- Uses fixture generator from internal/testutil/fixtures\n- Follows existing setupBenchDB() pattern\n- Handles database cleanup\n\nThese helpers reduce duplication across new benchmark functions and provide consistent large-scale database setup.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-13T22:22:55.694834-08:00","updated_at":"2025-11-13T23:13:41.244758-08:00","closed_at":"2025-11-13T23:13:41.244758-08:00","dependencies":[{"issue_id":"bd-3tfh","depends_on_id":"bd-m62x","type":"blocks","created_at":"2025-11-13T22:24:02.632994-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-3xl","title":"bd doctor: per-fix confirmation mode","description":"Add an interactive mode where users can approve/reject each fix individually rather than all-or-nothing. Currently --fix prompts once for all fixes. This would give users more control over which repairs to apply, especially useful when some fixes may have side effects.","status":"closed","priority":4,"issue_type":"feature","created_at":"2025-12-02T12:52:38.137201-08:00","updated_at":"2025-12-13T08:15:21.911204+11:00","closed_at":"2025-12-03T22:17:11.405848-08:00"}
|
||||
{"id":"bd-40a0","title":"bd doctor should check for multiple DBs, multiple JSONLs, daemon health","description":"","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-10-31T21:16:47.042913-07:00","updated_at":"2025-10-31T21:21:27.093525-07:00","closed_at":"2025-10-31T21:21:27.093525-07:00"}
|
||||
{"id":"bd-442","title":"(deleted)","description":"","status":"tombstone","priority":0,"issue_type":"task","created_at":"2025-12-14T16:43:19.41799-08:00","updated_at":"2025-12-14T16:43:19.41799-08:00","deleted_at":"0001-01-01T00:00:00Z"}
|
||||
{"id":"bd-4462","title":"Test basic bd commands in WASM (init, create, list)","description":"Compile and verify basic bd functionality works in WASM:\n- Test bd init --quiet\n- Test bd create with simple issue\n- Test bd list --json output\n- Verify SQLite database creation and queries work\n- Document any runtime issues or workarounds needed","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-02T21:58:07.291771-08:00","updated_at":"2025-11-02T23:07:10.273212-08:00","closed_at":"2025-11-02T23:07:10.273212-08:00","dependencies":[{"issue_id":"bd-4462","depends_on_id":"bd-44d0","type":"parent-child","created_at":"2025-11-02T22:23:49.448668-08:00","created_by":"stevey"},{"issue_id":"bd-4462","depends_on_id":"bd-b4b0","type":"blocks","created_at":"2025-11-02T22:23:55.596771-08:00","created_by":"stevey"}]}
|
||||
{"id":"bd-44d0","title":"WASM port of bd for Claude Code Web sandboxes","description":"Enable beads to work in Claude Code Web sandboxes by compiling bd to WebAssembly.\n\n## Problem\nClaude Code Web sandboxes cannot install bd CLI due to network restrictions:\n- GitHub releases return 403\n- go install fails with DNS errors\n- Binary cannot be downloaded\n\n## Solution\nCompile bd Go codebase to WASM, publish to npm as drop-in replacement.\n\n## Technical Approach\n- Use GOOS=js GOARCH=wasm to compile bd\n- modernc.org/sqlite already supports js/wasm target\n- Publish to npm as bd-wasm package\n- Full feature parity with bd CLI\n\n## Success Criteria\n- bd-wasm installs via npm in web sandbox\n- All core bd commands work identically\n- JSONL output matches native bd\n- Performance within 2x of native","notes":"WASM port abandoned - Claude Code Web has full VMs not browser restrictions. Better: npm + native binary","status":"closed","priority":0,"issue_type":"epic","created_at":"2025-11-02T18:32:27.660794-08:00","updated_at":"2025-12-13T18:00:31.061977-08:00","closed_at":"2025-11-02T23:36:38.679515-08:00"}
|
||||
{"id":"bd-44e","title":"Ensure deletions.jsonl is tracked in git","description":"Parent: bd-imj\n\nEnsure deletions.jsonl is tracked in git (not ignored).\n\nUpdate bd init and gitignore upgrade logic to:\n1. NOT add deletions.jsonl to .gitignore\n2. Ensure it is committed alongside beads.jsonl\n\nThe file must be in git for cross-clone propagation to work.\n\nAcceptance criteria:\n- bd init does not ignore deletions.jsonl\n- Existing .gitignore files are not broken\n- File appears in git status when modified","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-25T09:57:21.663196-08:00","updated_at":"2025-11-25T14:55:43.225883-08:00","closed_at":"2025-11-25T14:55:43.225883-08:00"}
|
||||
@@ -865,7 +864,7 @@
|
||||
{"id":"bd-zo7o","title":"Create multi-agent race condition test","description":"Automated test that runs 2+ agents simultaneously to verify collision prevention.\n\nAcceptance Criteria:\n- Script spawns 2 agents in parallel\n- Both try to claim same issue\n- Only one succeeds (via reservation)\n- Other agent skips to different work\n- Verify in JSONL that no duplicate claims\n- Test with Agent Mail enabled/disabled\n\nFile: tests/integration/test_agent_race.py\n\nSuccess Metric: Zero duplicate claims with Agent Mail, collisions without it","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-07T22:43:21.360663-08:00","updated_at":"2025-11-08T00:34:14.40119-08:00","closed_at":"2025-11-08T00:34:14.40119-08:00","dependencies":[{"issue_id":"bd-zo7o","depends_on_id":"bd-fzbg","type":"blocks","created_at":"2025-11-07T22:43:21.361571-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-zpnq","title":"Daemons don't exit when parent process dies, causing accumulation and race conditions","description":"Multiple daemon processes accumulate over time because daemons don't automatically stop when their parent process (e.g., coding agent) is killed. This causes:\n\n1. Race conditions: 8+ daemons watching same .beads/beads.db, each with own 30s debounce timer\n2. Git conflicts: Multiple daemons racing to commit/push .beads/issues.jsonl\n3. Resource waste: Orphaned daemons from sessions days/hours old still running\n\nExample: User had 8 daemons from multiple sessions (12:37AM, 7:20PM, 7:22PM, 7:47PM, 9:19PM yesterday + 9:54AM, 10:55AM today).\n\nSolutions to consider:\n1. Track parent PID and exit when parent dies\n2. Use single global daemon instead of per-session\n3. Document manual cleanup: pkill -f \"bd daemon\"\n4. Add daemon lifecycle management (auto-cleanup of stale daemons)","notes":"Implementation complete:\n\n1. Added ParentPID field to DaemonLockInfo struct (stored in daemon.lock JSON)\n2. Daemon now tracks parent PID via os.Getppid() at startup\n3. Both event loops (polling and event-driven) check parent process every 10 seconds\n4. Daemon gracefully exits if parent process dies (detected via isProcessRunning check)\n5. Handles edge cases:\n - ParentPID=0: Older daemons without tracking (ignored)\n - ParentPID=1: Adopted by init means parent died (exits)\n - Otherwise checks if parent process is still running\n\nThe fix prevents daemon accumulation by ensuring orphaned daemons automatically exit within 10 seconds of parent death.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-07T18:48:41.65456-08:00","updated_at":"2025-11-07T18:53:26.382573-08:00","closed_at":"2025-11-07T18:53:26.382573-08:00"}
|
||||
{"id":"bd-zqmb","title":"Fix goroutine leak in daemon restart","description":"Fire-and-forget goroutine in daemon restart leaks on every restart.\n\nLocation: cmd/bd/daemons.go:251\n\nProblem:\ngo func() { _ = daemonCmd.Wait() }()\n\n- Spawns goroutine without timeout or cancellation\n- If daemon command never completes, goroutine leaks forever\n- Each daemon restart leaks one more goroutine\n\nSolution: Add timeout and cleanup:\ngo func() {\n done := make(chan struct{})\n go func() {\n _ = daemonCmd.Wait()\n close(done)\n }()\n \n select {\n case \u003c-done:\n // Exited normally\n case \u003c-time.After(10 * time.Second):\n // Timeout - daemon should have forked by now\n _ = daemonCmd.Process.Kill()\n }\n}()\n\nImpact: Goroutine leak on every daemon restart\n\nEffort: 2 hours","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-16T14:52:01.897215-08:00","updated_at":"2025-11-16T15:04:00.497517-08:00","closed_at":"2025-11-16T15:04:00.497517-08:00"}
|
||||
{"id":"bd-zsle","title":"GH#516: Automate 'landing the plane' setup in AGENTS.md","description":"bd init or /beads:init should auto-add landing-the-plane instructions to AGENTS.md (and @AGENTS.md for web Claude). Reduces manual setup. See: https://github.com/steveyegge/beads/issues/516","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-14T16:31:57.541154-08:00","updated_at":"2025-12-14T16:31:57.541154-08:00"}
|
||||
{"id":"bd-zsle","title":"GH#516: Automate 'landing the plane' setup in AGENTS.md","description":"bd init or /beads:init should auto-add landing-the-plane instructions to AGENTS.md (and @AGENTS.md for web Claude). Reduces manual setup. See: https://github.com/steveyegge/beads/issues/516","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-14T16:31:57.541154-08:00","updated_at":"2025-12-14T17:20:34.891647-08:00","closed_at":"2025-12-14T17:20:34.891647-08:00"}
|
||||
{"id":"bd-zsz","title":"Add --parent flag to bd onboard output","description":"bd onboard didn't document --parent flag for epic subtasks, causing AI agents to guess wrong syntax. Added --parent example and CLI help section pointing to bd \u003ccmd\u003e --help.\n\nFixes: https://github.com/steveyegge/beads/issues/402","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-27T13:01:51.366625-08:00","updated_at":"2025-11-27T13:02:02.018003-08:00","closed_at":"2025-11-27T13:02:02.018003-08:00"}
|
||||
{"id":"bd-zvg","title":"Design tombstone merge semantics","description":"Define how tombstones participate in 3-way merge. Key question: does tombstone always win over live issue (like current deletion-wins rule), or should we use updated_at comparison? Consider: tombstone with older timestamp vs live issue with newer edits. Recommendation: tombstone wins unless expired, matching current behavior but with longer TTL window.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-05T13:43:13.522002-08:00","updated_at":"2025-12-05T15:01:30.998256-08:00","closed_at":"2025-12-05T15:01:30.998256-08:00"}
|
||||
{"id":"bd-zwpw","title":"Test dependency child","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-05T11:23:05.998311-08:00","updated_at":"2025-11-05T11:23:30.389454-08:00","closed_at":"2025-11-05T11:23:30.389454-08:00","dependencies":[{"issue_id":"bd-zwpw","depends_on_id":"bd-k0j9","type":"blocks","created_at":"2025-11-05T11:23:05.998981-08:00","created_by":"daemon"}]}
|
||||
|
||||
117
cmd/bd/init.go
117
cmd/bd/init.go
@@ -423,6 +423,15 @@ With --stealth: configures global git settings for invisible beads usage:
|
||||
}
|
||||
}
|
||||
|
||||
// Add "landing the plane" instructions to AGENTS.md and @AGENTS.md
|
||||
// Skip in stealth mode (user wants invisible setup) and quiet mode (suppress all output)
|
||||
if !stealth {
|
||||
if err := addLandingThePlaneInstructions(!quiet); err != nil && !quiet {
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Fprintf(os.Stderr, "%s Failed to add landing-the-plane instructions: %v\n", yellow("⚠"), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Skip output if quiet mode
|
||||
if quiet {
|
||||
return
|
||||
@@ -1550,6 +1559,114 @@ Aborting.`, yellow("⚠"), dbPath, cyan("bd list"), prefix)
|
||||
}
|
||||
|
||||
|
||||
// landingThePlaneSection is the "landing the plane" instructions for AI agents
|
||||
// This gets appended to AGENTS.md and @AGENTS.md during bd init
|
||||
const landingThePlaneSection = `
|
||||
## Landing the Plane (Session Completion)
|
||||
|
||||
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until ` + "`git push`" + ` succeeds.
|
||||
|
||||
**MANDATORY WORKFLOW:**
|
||||
|
||||
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
||||
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
||||
3. **Update issue status** - Close finished work, update in-progress items
|
||||
4. **PUSH TO REMOTE** - This is MANDATORY:
|
||||
` + "```bash" + `
|
||||
git pull --rebase
|
||||
bd sync
|
||||
git push
|
||||
git status # MUST show "up to date with origin"
|
||||
` + "```" + `
|
||||
5. **Clean up** - Clear stashes, prune remote branches
|
||||
6. **Verify** - All changes committed AND pushed
|
||||
7. **Hand off** - Provide context for next session
|
||||
|
||||
**CRITICAL RULES:**
|
||||
- Work is NOT complete until ` + "`git push`" + ` succeeds
|
||||
- NEVER stop before pushing - that leaves work stranded locally
|
||||
- NEVER say "ready to push when you are" - YOU must push
|
||||
- If push fails, resolve and retry until it succeeds
|
||||
`
|
||||
|
||||
// addLandingThePlaneInstructions adds "landing the plane" instructions to AGENTS.md and @AGENTS.md
|
||||
func addLandingThePlaneInstructions(verbose bool) error {
|
||||
// Files to update (AGENTS.md and @AGENTS.md for web Claude)
|
||||
agentFiles := []string{"AGENTS.md", "@AGENTS.md"}
|
||||
|
||||
for _, filename := range agentFiles {
|
||||
if err := updateAgentFile(filename, verbose); err != nil {
|
||||
// Non-fatal - continue with other files
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to update %s: %v\n", filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateAgentFile creates or updates an agent instructions file with landing the plane section
|
||||
func updateAgentFile(filename string, verbose bool) error {
|
||||
// Check if file exists
|
||||
content, err := os.ReadFile(filename)
|
||||
if os.IsNotExist(err) {
|
||||
// File doesn't exist - create it with basic structure
|
||||
newContent := fmt.Sprintf(`# Agent Instructions
|
||||
|
||||
This project uses **bd** (beads) for issue tracking. Run ` + "`bd onboard`" + ` to get started.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
` + "```bash" + `
|
||||
bd ready # Find available work
|
||||
bd show <id> # View issue details
|
||||
bd update <id> --status in_progress # Claim work
|
||||
bd close <id> # Complete work
|
||||
bd sync # Sync with git
|
||||
` + "```" + `
|
||||
%s
|
||||
`, landingThePlaneSection)
|
||||
|
||||
// #nosec G306 - markdown needs to be readable
|
||||
if err := os.WriteFile(filename, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create %s: %w", filename, err)
|
||||
}
|
||||
if verbose {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf(" %s Created %s with landing-the-plane instructions\n", green("✓"), filename)
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to read %s: %w", filename, err)
|
||||
}
|
||||
|
||||
// File exists - check if it already has landing the plane section
|
||||
if strings.Contains(string(content), "Landing the Plane") {
|
||||
if verbose {
|
||||
fmt.Printf(" %s already has landing-the-plane instructions\n", filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append the landing the plane section
|
||||
newContent := string(content)
|
||||
if !strings.HasSuffix(newContent, "\n") {
|
||||
newContent += "\n"
|
||||
}
|
||||
newContent += landingThePlaneSection
|
||||
|
||||
// #nosec G306 - markdown needs to be readable
|
||||
if err := os.WriteFile(filename, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to update %s: %w", filename, err)
|
||||
}
|
||||
if verbose {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf(" %s Added landing-the-plane instructions to %s\n", green("✓"), filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupClaudeSettings creates or updates .claude/settings.local.json with onboard instruction
|
||||
func setupClaudeSettings(verbose bool) error {
|
||||
claudeDir := ".claude"
|
||||
|
||||
Reference in New Issue
Block a user