diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 08ede3c..d672010 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -22,7 +22,7 @@ {"id":"bd-3c7","title":"Handle edge case: empty or missing ~/projects/ directory","description":"## Overview\nHandle the case where ~/projects/ doesn't exist or is empty gracefully.\n\n## Background\nNot all users will have ~/projects/ configured. The spawn feature should degrade gracefully:\n- Return empty projects list (not error)\n- Dashboard shows helpful message\n- No crashes or confusing errors\n\n## Server Side\nload_projects_cache() already handles this:\n```python\nexcept OSError:\n _projects_cache = []\n```\n\n## Dashboard Side\nSpawnModal should handle empty projects list:\n1. Projects dropdown is empty\n2. Show message: 'No projects found in ~/projects/'\n3. Spawn button remains disabled (no selection possible)\n\n## Implementation\nIn SpawnModal.js, add condition:\n```javascript\n${projects.length === 0 && !loadingProjects && html`\n
\n No projects found in ~/projects/\n
\n`}\n```\n\n## Edge Cases\n1. ~/projects/ doesn't exist -> empty list\n2. ~/projects/ exists but empty -> empty list\n3. ~/projects/ exists, only hidden dirs -> empty list (they're excluded)\n4. ~/projects/ exists with permission error -> empty list (OSError caught)\n\n## Testing\n1. Temporarily rename ~/projects/\n2. Restart server\n3. Open spawn modal on All Projects\n4. Verify helpful message shown\n5. Verify no crash\n6. Restore ~/projects/\n\n## Success Criteria\n- Server doesn't crash without ~/projects/\n- Dashboard shows informative message\n- No confusing error toasts","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-26T21:43:13.655184Z","created_by":"tayloreernisse","updated_at":"2026-02-26T22:08:26.546217Z","closed_at":"2026-02-26T22:08:26.546165Z","close_reason":"Implemented empty projects edge case: server already handled OSError/missing dir gracefully, added empty-state message to SpawnModal, added tests for missing dir + only-hidden-dirs + empty API responses","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-3c7","depends_on_id":"bd-15z","type":"blocks","created_at":"2026-02-26T21:43:16.434386Z","created_by":"tayloreernisse"}]} {"id":"bd-3cc","title":"E2E tests for autocomplete workflow","description":"## Overview\nCreate end-to-end test script that validates the complete autocomplete workflow from typing to insertion.\n\n## Background\nThe plan includes a manual testing checklist (lines 462-480). We should automate key workflows to ensure the feature works end-to-end.\n\n## Test Scenarios (from plan's testing checklist)\n\n### Core Flow\n```\n1. Claude session: Type \"/\" -> dropdown appears with Claude skills\n2. Codex session: Type \"$\" -> dropdown appears with Codex skills\n3. Claude session: Type \"$\" -> nothing happens (wrong trigger)\n4. Type \"/com\" -> list filters to skills containing \"com\"\n5. Mid-message: Type \"please run /commit\" -> autocomplete triggers on \"/\"\n6. Arrow keys navigate, Enter selects\n7. Escape dismisses without selection\n8. Click outside dismisses\n9. Selected skill shows as \"{trigger}skill-name \" in input\n10. Verify alphabetical ordering of skills\n11. Verify vertical scroll with many skills\n```\n\n### Edge Cases (from plan section)\n```\n- Session without skills (dropdown shows \"No skills available\")\n- Single skill (still shows dropdown)\n- Very long skill descriptions (CSS truncates with ellipsis - visual check)\n- Multiple triggers in one message (each \"/\" can trigger independently)\n- Backspace over trigger (dismisses autocomplete)\n```\n\n### Multiple Triggers Test (important edge case)\nUser types: \"first /commit then /review-pr finally\"\n- First \"/\" at position 6 can trigger\n- After inserting \"/commit \", cursor at position 14\n- Second \"/\" at position after text can trigger again\n- Verify each trigger works independently\n\n## Implementation Approach\nUse the Playwright MCP tools to:\n1. Navigate to dashboard\n2. Open a session modal\n3. Type trigger character\n4. Verify dropdown appears\n5. Navigate with arrows\n6. Select with Enter\n7. Verify insertion\n\n## Logging Requirements\n- Log each step being performed\n- Log expected vs actual behavior\n- Log timing for performance visibility\n- Log any errors with context\n\n## Test Script Location\ntests/e2e/test_autocomplete.py or similar\n\n## Success Criteria\n- All core scenarios pass\n- Edge cases handled\n- Detailed logging for debugging\n- Can run in CI environment","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-26T20:11:47.556903Z","created_by":"tayloreernisse","updated_at":"2026-02-26T22:04:06.299931Z","closed_at":"2026-02-26T22:04:06.299744Z","close_reason":"Implemented 50 E2E tests (14 server-side Python, 36 client-side JS) covering: /api/skills endpoint, trigger detection, keyboard navigation, skill insertion, alphabetical ordering, cross-agent isolation, edge cases, and full workflow simulation","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-3cc","depends_on_id":"bd-3ny","type":"blocks","created_at":"2026-02-26T20:11:50.670698Z","created_by":"tayloreernisse"}]} {"id":"bd-3eu","title":"Implement Codex skill enumeration","description":"## Overview\nImplement _enumerate_codex_skills() method in SkillsMixin to discover skills from both the curated cache and user directory.\n\n## Background\nCodex stores skills in two locations:\n1. **Curated cache**: ~/.codex/vendor_imports/skills-curated-cache.json (pre-installed skills)\n2. **User skills**: ~/.codex/skills/*/ (user-created or installed)\n\nBoth need to be combined for the full skills list.\n\n## Implementation (from plan IMP-1)\n```python\ndef _enumerate_codex_skills(self):\n skills = []\n \n # 1. Curated skills from cache\n cache_file = Path.home() / '.codex/vendor_imports/skills-curated-cache.json'\n if cache_file.exists():\n try:\n data = json.loads(cache_file.read_text())\n for skill in data.get('skills', []):\n skills.append({\n 'name': skill.get('id', skill.get('name', '')),\n 'description': skill.get('shortDescription', skill.get('description', ''))[:100]\n })\n except (json.JSONDecodeError, OSError):\n pass # Continue without curated skills\n \n # 2. User-installed skills\n user_skills_dir = Path.home() / '.codex/skills'\n if user_skills_dir.exists():\n for skill_dir in user_skills_dir.iterdir():\n if skill_dir.is_dir() and not skill_dir.name.startswith('.'):\n skill_md = skill_dir / 'SKILL.md'\n description = ''\n if skill_md.exists():\n try:\n for line in skill_md.read_text().splitlines():\n line = line.strip()\n if line and not line.startswith('#'):\n description = line[:100]\n break\n except OSError:\n pass\n skills.append({\n 'name': skill_dir.name,\n 'description': description or f'User skill: {skill_dir.name}'\n })\n \n return skills\n```\n\n## Key Decisions\n- **Cache file structure**: Expected format {skills: [{id, shortDescription}, ...]}\n- **Fallback for missing fields**: Use 'name' if 'id' missing, 'description' if 'shortDescription' missing\n- **No deduplication**: If curated and user skills share a name, both appear (per Known Limitations)\n- **Error resilience**: JSON parse errors don't prevent user skills from loading\n\n## Out of Scope (per plan)\n- Duplicate skill names are NOT deduplicated\n- Server-side caching of enumeration results\n\n## Success Criteria\n- Returns combined list from cache + user directory\n- Handles missing files/directories gracefully\n- Truncates descriptions to 100 chars\n- JSON parse errors don't crash enumeration","status":"closed","priority":1,"issue_type":"task","created_at":"2026-02-26T20:07:58.579276Z","created_by":"tayloreernisse","updated_at":"2026-02-26T21:43:21.057360Z","closed_at":"2026-02-26T21:43:21.057312Z","close_reason":"Implemented _enumerate_codex_skills: reads curated cache + user skills directory, handles JSON errors gracefully","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-3eu","depends_on_id":"bd-3q1","type":"blocks","created_at":"2026-02-26T20:08:01.832043Z","created_by":"tayloreernisse"}]} -{"id":"bd-3g8","title":"Slice 3 Integration: Polish and edge cases complete","description":"## Overview\nFinal integration checkpoint verifying all polish and edge case handling.\n\n## Background\nSlice 3 ensures the feature handles edge cases gracefully and provides a polished UX. This bead verifies:\n- Empty projects handling\n- Visual feedback for new agents\n- Special character support\n- Zellij metadata correctness\n- Warning banners\n\n## Verification Checklist\n\n### 1. Empty Projects Directory\n- [ ] Test with empty ~/projects/\n- [ ] Modal shows 'No projects found' message\n- [ ] No crash or confusing error\n\n### 2. Visual Feedback\n- [ ] Spawn an agent\n- [ ] Watch session cards area\n- [ ] New card has highlight animation\n- [ ] Animation fades after ~2s\n\n### 3. Special Characters\n- [ ] Create test project with hyphen: my-test\n- [ ] Spawn agent for it -> success\n- [ ] Create test project with underscore: my_test\n- [ ] Spawn agent for it -> success\n- [ ] Test with path traversal ../etc -> rejected\n\n### 4. Zellij Metadata\n- [ ] Spawn agent\n- [ ] Check session file: jq '.zellij_session' \n- [ ] Should be 'infra'\n- [ ] Check session file: jq '.zellij_pane'\n- [ ] Should be valid pane ID\n\n### 5. Dashboard Response\n- [ ] Spawn agent\n- [ ] Wait for AskUserQuestion\n- [ ] Submit response via dashboard\n- [ ] Response appears in Zellij pane\n\n### 6. Zellij Unavailable Warning\n- [ ] Stop Zellij session (zellij kill-session infra)\n- [ ] Reload dashboard\n- [ ] Warning banner should appear\n- [ ] Banner text clear and actionable\n- [ ] Start Zellij session\n- [ ] Warning banner disappears\n\n### 7. Projects Cache Refresh\n- [ ] Add new project to ~/projects/\n- [ ] POST /api/projects/refresh\n- [ ] New project appears in dropdown\n\n### 8. Background Refresh\n- [ ] Add project without manual refresh\n- [ ] Wait 5 minutes\n- [ ] Project appears in dropdown\n\n## Full Feature Walkthrough\n1. Start Zellij session 'infra'\n2. Start AMC server\n3. Open dashboard\n4. Select project in sidebar\n5. Click '+ New Agent'\n6. Select 'Claude'\n7. Click 'Spawn'\n8. See success toast\n9. See agent appear in Zellij\n10. See agent card in dashboard (possibly with highlight)\n11. Wait for AskUserQuestion\n12. Submit response\n13. Response appears in agent\n\n## Acceptance Criteria Covered\n- AC-15: Agent appears within 10 seconds\n- AC-16, AC-17: Correct Zellij metadata\n- AC-42: Warning banner\n\n## Success Criteria\nComplete feature works end-to-end with all edge cases handled gracefully.","status":"open","priority":1,"issue_type":"task","created_at":"2026-02-26T21:45:39.460751Z","created_by":"tayloreernisse","updated_at":"2026-02-26T21:45:45.059903Z","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-3g8","depends_on_id":"bd-14p","type":"blocks","created_at":"2026-02-26T21:45:44.875703Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-2cw","type":"blocks","created_at":"2026-02-26T21:45:44.790681Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-30o","type":"blocks","created_at":"2026-02-26T21:45:44.962392Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-3c7","type":"blocks","created_at":"2026-02-26T21:45:44.730490Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-3ke","type":"blocks","created_at":"2026-02-26T21:45:45.059877Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-zgt","type":"blocks","created_at":"2026-02-26T21:45:44.592034Z","created_by":"tayloreernisse"}]} +{"id":"bd-3g8","title":"Slice 3 Integration: Polish and edge cases complete","description":"## Overview\nFinal integration checkpoint verifying all polish and edge case handling.\n\n## Background\nSlice 3 ensures the feature handles edge cases gracefully and provides a polished UX. This bead verifies:\n- Empty projects handling\n- Visual feedback for new agents\n- Special character support\n- Zellij metadata correctness\n- Warning banners\n\n## Verification Checklist\n\n### 1. Empty Projects Directory\n- [ ] Test with empty ~/projects/\n- [ ] Modal shows 'No projects found' message\n- [ ] No crash or confusing error\n\n### 2. Visual Feedback\n- [ ] Spawn an agent\n- [ ] Watch session cards area\n- [ ] New card has highlight animation\n- [ ] Animation fades after ~2s\n\n### 3. Special Characters\n- [ ] Create test project with hyphen: my-test\n- [ ] Spawn agent for it -> success\n- [ ] Create test project with underscore: my_test\n- [ ] Spawn agent for it -> success\n- [ ] Test with path traversal ../etc -> rejected\n\n### 4. Zellij Metadata\n- [ ] Spawn agent\n- [ ] Check session file: jq '.zellij_session' \n- [ ] Should be 'infra'\n- [ ] Check session file: jq '.zellij_pane'\n- [ ] Should be valid pane ID\n\n### 5. Dashboard Response\n- [ ] Spawn agent\n- [ ] Wait for AskUserQuestion\n- [ ] Submit response via dashboard\n- [ ] Response appears in Zellij pane\n\n### 6. Zellij Unavailable Warning\n- [ ] Stop Zellij session (zellij kill-session infra)\n- [ ] Reload dashboard\n- [ ] Warning banner should appear\n- [ ] Banner text clear and actionable\n- [ ] Start Zellij session\n- [ ] Warning banner disappears\n\n### 7. Projects Cache Refresh\n- [ ] Add new project to ~/projects/\n- [ ] POST /api/projects/refresh\n- [ ] New project appears in dropdown\n\n### 8. Background Refresh\n- [ ] Add project without manual refresh\n- [ ] Wait 5 minutes\n- [ ] Project appears in dropdown\n\n## Full Feature Walkthrough\n1. Start Zellij session 'infra'\n2. Start AMC server\n3. Open dashboard\n4. Select project in sidebar\n5. Click '+ New Agent'\n6. Select 'Claude'\n7. Click 'Spawn'\n8. See success toast\n9. See agent appear in Zellij\n10. See agent card in dashboard (possibly with highlight)\n11. Wait for AskUserQuestion\n12. Submit response\n13. Response appears in agent\n\n## Acceptance Criteria Covered\n- AC-15: Agent appears within 10 seconds\n- AC-16, AC-17: Correct Zellij metadata\n- AC-42: Warning banner\n\n## Success Criteria\nComplete feature works end-to-end with all edge cases handled gracefully.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-02-26T21:45:39.460751Z","created_by":"tayloreernisse","updated_at":"2026-02-26T22:18:20.391913Z","closed_at":"2026-02-26T22:18:20.391866Z","close_reason":"All polish and edge cases verified: Empty projects shows friendly message, newlySpawned animation works, path traversal/special chars validated in spawn.py, zellij metadata fully supported, warning banner when Zellij unavailable. Feature complete.","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-3g8","depends_on_id":"bd-14p","type":"blocks","created_at":"2026-02-26T21:45:44.875703Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-2cw","type":"blocks","created_at":"2026-02-26T21:45:44.790681Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-30o","type":"blocks","created_at":"2026-02-26T21:45:44.962392Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-3c7","type":"blocks","created_at":"2026-02-26T21:45:44.730490Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-3ke","type":"blocks","created_at":"2026-02-26T21:45:45.059877Z","created_by":"tayloreernisse"},{"issue_id":"bd-3g8","depends_on_id":"bd-zgt","type":"blocks","created_at":"2026-02-26T21:45:44.592034Z","created_by":"tayloreernisse"}]} {"id":"bd-3ke","title":"Dashboard warning banner when Zellij session unavailable","description":"## Overview\nShow warning banner in dashboard when Zellij session 'infra' is unavailable.\n\n## Background\nPer AC-42, the dashboard should warn users when spawning won't work because the Zellij session doesn't exist. This prevents confusion when spawn attempts fail.\n\n## Implementation\n\n### Health Polling\nAdd periodic health check in App.js:\n```javascript\nconst [zelijAvailable, setZelijAvailable] = useState(true);\n\nuseEffect(() => {\n const checkHealth = async () => {\n try {\n const response = await fetch('/api/health');\n const data = await response.json();\n setZelijAvailable(data.zellij_available);\n } catch {\n // Server unreachable - handled elsewhere\n }\n };\n \n checkHealth();\n const interval = setInterval(checkHealth, 30000); // Check every 30s\n return () => clearInterval(interval);\n}, []);\n```\n\n### Warning Banner\nAdd before main content:\n```javascript\n${!zelijAvailable && html`\nzellij attach infra\n