chore: close Phase 5.5 epic (bd-1b6k) — 63 reliability tests

This commit is contained in:
teernisse
2026-02-19 07:49:22 -05:00
parent 01491b4180
commit 8d24138655
2 changed files with 2 additions and 2 deletions

View File

@@ -23,7 +23,7 @@
{"id":"bd-1au9","title":"Audit and improve test coverage across ingestion module","description":"During code reorganization, discovered that ingestion/issues.rs has only 4 tests covering passes_cursor_filter, while 10 production functions (~400 lines) are untested:\n\nUNTESTED FUNCTIONS in ingestion/issues.rs:\n- ingest_issues() - main async pipeline with cursor-based pagination, shutdown handling\n- process_single_issue() - transforms GitLab issue, wraps in transaction\n- process_issue_in_transaction() - DB upsert with ON CONFLICT, label/assignee/milestone association, dirty tracking\n- upsert_label_tx() - label upsert with INSERT OR IGNORE + created count tracking\n- link_issue_label_tx() - issue-label junction table insert\n- upsert_milestone_tx() - milestone upsert with RETURNING id\n- get_sync_cursor() - reads sync_cursors table for incremental sync\n- update_sync_cursor() - writes sync cursor with tie-breaker ID\n- get_issues_needing_discussion_sync() - identifies issues needing discussion refresh\n- parse_timestamp() - RFC3339 parsing with error wrapping\n\nLIKELY SIMILAR GAPS in sibling files:\n- ingestion/merge_requests.rs (479 lines) - parallel structure to issues.rs\n- ingestion/discussions.rs (469 lines prod code) - discussion upsert pipeline\n- ingestion/mr_discussions.rs (738 lines prod before tests) - MR discussion pipeline\n- ingestion/orchestrator.rs (1703 lines) - full pipeline orchestration\n\nThe ingestion module handles the most critical data path (GitLab API -> SQLite) yet relies primarily on integration-level orchestrator tests rather than unit tests for individual functions.\n\nPRIORITY AREAS:\n1. DB upsert logic with ON CONFLICT handling (data correctness)\n2. Cursor-based pagination (incremental sync correctness)\n3. Label/milestone/assignee association (relational integrity)\n4. Dirty tracker marking after upserts (document pipeline triggering)\n5. Discussion sync queue population (cascading sync correctness)\n6. Error handling paths (invalid timestamps, missing data)\n\nAPPROACH: Use in-memory SQLite (create_connection(Path::new(\":memory:\")) + run_migrations) for unit tests. See existing patterns in core/db_tests.rs and documents/regenerator_tests.rs.","status":"open","priority":2,"issue_type":"task","created_at":"2026-02-13T00:53:15.302370Z","created_by":"tayloreernisse","updated_at":"2026-02-13T00:53:15.305167Z","compaction_level":0,"original_size":0,"labels":["testing"]} {"id":"bd-1au9","title":"Audit and improve test coverage across ingestion module","description":"During code reorganization, discovered that ingestion/issues.rs has only 4 tests covering passes_cursor_filter, while 10 production functions (~400 lines) are untested:\n\nUNTESTED FUNCTIONS in ingestion/issues.rs:\n- ingest_issues() - main async pipeline with cursor-based pagination, shutdown handling\n- process_single_issue() - transforms GitLab issue, wraps in transaction\n- process_issue_in_transaction() - DB upsert with ON CONFLICT, label/assignee/milestone association, dirty tracking\n- upsert_label_tx() - label upsert with INSERT OR IGNORE + created count tracking\n- link_issue_label_tx() - issue-label junction table insert\n- upsert_milestone_tx() - milestone upsert with RETURNING id\n- get_sync_cursor() - reads sync_cursors table for incremental sync\n- update_sync_cursor() - writes sync cursor with tie-breaker ID\n- get_issues_needing_discussion_sync() - identifies issues needing discussion refresh\n- parse_timestamp() - RFC3339 parsing with error wrapping\n\nLIKELY SIMILAR GAPS in sibling files:\n- ingestion/merge_requests.rs (479 lines) - parallel structure to issues.rs\n- ingestion/discussions.rs (469 lines prod code) - discussion upsert pipeline\n- ingestion/mr_discussions.rs (738 lines prod before tests) - MR discussion pipeline\n- ingestion/orchestrator.rs (1703 lines) - full pipeline orchestration\n\nThe ingestion module handles the most critical data path (GitLab API -> SQLite) yet relies primarily on integration-level orchestrator tests rather than unit tests for individual functions.\n\nPRIORITY AREAS:\n1. DB upsert logic with ON CONFLICT handling (data correctness)\n2. Cursor-based pagination (incremental sync correctness)\n3. Label/milestone/assignee association (relational integrity)\n4. Dirty tracker marking after upserts (document pipeline triggering)\n5. Discussion sync queue population (cascading sync correctness)\n6. Error handling paths (invalid timestamps, missing data)\n\nAPPROACH: Use in-memory SQLite (create_connection(Path::new(\":memory:\")) + run_migrations) for unit tests. See existing patterns in core/db_tests.rs and documents/regenerator_tests.rs.","status":"open","priority":2,"issue_type":"task","created_at":"2026-02-13T00:53:15.302370Z","created_by":"tayloreernisse","updated_at":"2026-02-13T00:53:15.305167Z","compaction_level":0,"original_size":0,"labels":["testing"]}
{"id":"bd-1b0n","title":"OBSERV: Print human-readable timing summary after interactive sync","description":"## Background\nInteractive users want a quick timing summary after sync completes. This is the human-readable equivalent of meta.stages in robot JSON. Gated behind IngestDisplay::show_text so it doesn't appear in -q, robot, or progress_only modes.\n\n## Approach\nAdd a function to format and print the timing summary, called from run_sync() after the pipeline completes:\n\n```rust\nfn print_timing_summary(stages: &[StageTiming], total_elapsed: Duration) {\n eprintln!();\n eprintln!(\"Sync complete in {:.1}s\", total_elapsed.as_secs_f64());\n for stage in stages {\n let dots = \".\".repeat(20_usize.saturating_sub(stage.name.len()));\n eprintln!(\n \" {} {} {:.1}s ({} items{})\",\n stage.name,\n dots,\n stage.elapsed_ms as f64 / 1000.0,\n stage.items_processed,\n if stage.errors > 0 { format!(\", {} errors\", stage.errors) } else { String::new() },\n );\n }\n}\n```\n\nCall in run_sync() (src/cli/commands/sync.rs), after pipeline and before return:\n```rust\nif display.show_text {\n let stages = metrics_handle.extract_timings();\n print_timing_summary(&stages, start.elapsed());\n}\n```\n\nOutput format per PRD Section 4.6.4:\n```\nSync complete in 45.2s\n Ingest issues .... 12.3s (150 items, 42 discussions)\n Ingest MRs ....... 18.9s (85 items, 1 error)\n Generate docs .... 8.5s (235 documents)\n Embed ............ 5.5s (1024 chunks)\n```\n\n## Acceptance Criteria\n- [ ] Interactive lore sync prints timing summary to stderr after completion\n- [ ] Summary shows total time and per-stage breakdown\n- [ ] lore -q sync does NOT print timing summary\n- [ ] Robot mode does NOT print timing summary (only JSON)\n- [ ] Error counts shown when non-zero\n- [ ] cargo clippy --all-targets -- -D warnings passes\n\n## Files\n- src/cli/commands/sync.rs (add print_timing_summary function, call after pipeline)\n\n## TDD Loop\nRED: test_timing_summary_format (capture stderr, verify format matches PRD example pattern)\nGREEN: Implement print_timing_summary, gate behind display.show_text\nVERIFY: cargo test && cargo clippy --all-targets -- -D warnings\n\n## Edge Cases\n- Empty stages (e.g., sync with no projects configured): print \"Sync complete in 0.0s\" with no stage lines\n- Very fast stages (<1ms): show \"0.0s\" not scientific notation\n- Stage names with varying lengths: dot padding keeps alignment readable","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-04T15:54:32.109882Z","created_by":"tayloreernisse","updated_at":"2026-02-04T17:32:52.558314Z","closed_at":"2026-02-04T17:32:52.558264Z","close_reason":"Added print_timing_summary with per-stage breakdown (name, elapsed, items, errors, rate limits), nested sub-stage support, gated behind metrics Option","compaction_level":0,"original_size":0,"labels":["observability"],"dependencies":[{"issue_id":"bd-1b0n","depends_on_id":"bd-1zj6","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1b0n","depends_on_id":"bd-3er","type":"parent-child","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]} {"id":"bd-1b0n","title":"OBSERV: Print human-readable timing summary after interactive sync","description":"## Background\nInteractive users want a quick timing summary after sync completes. This is the human-readable equivalent of meta.stages in robot JSON. Gated behind IngestDisplay::show_text so it doesn't appear in -q, robot, or progress_only modes.\n\n## Approach\nAdd a function to format and print the timing summary, called from run_sync() after the pipeline completes:\n\n```rust\nfn print_timing_summary(stages: &[StageTiming], total_elapsed: Duration) {\n eprintln!();\n eprintln!(\"Sync complete in {:.1}s\", total_elapsed.as_secs_f64());\n for stage in stages {\n let dots = \".\".repeat(20_usize.saturating_sub(stage.name.len()));\n eprintln!(\n \" {} {} {:.1}s ({} items{})\",\n stage.name,\n dots,\n stage.elapsed_ms as f64 / 1000.0,\n stage.items_processed,\n if stage.errors > 0 { format!(\", {} errors\", stage.errors) } else { String::new() },\n );\n }\n}\n```\n\nCall in run_sync() (src/cli/commands/sync.rs), after pipeline and before return:\n```rust\nif display.show_text {\n let stages = metrics_handle.extract_timings();\n print_timing_summary(&stages, start.elapsed());\n}\n```\n\nOutput format per PRD Section 4.6.4:\n```\nSync complete in 45.2s\n Ingest issues .... 12.3s (150 items, 42 discussions)\n Ingest MRs ....... 18.9s (85 items, 1 error)\n Generate docs .... 8.5s (235 documents)\n Embed ............ 5.5s (1024 chunks)\n```\n\n## Acceptance Criteria\n- [ ] Interactive lore sync prints timing summary to stderr after completion\n- [ ] Summary shows total time and per-stage breakdown\n- [ ] lore -q sync does NOT print timing summary\n- [ ] Robot mode does NOT print timing summary (only JSON)\n- [ ] Error counts shown when non-zero\n- [ ] cargo clippy --all-targets -- -D warnings passes\n\n## Files\n- src/cli/commands/sync.rs (add print_timing_summary function, call after pipeline)\n\n## TDD Loop\nRED: test_timing_summary_format (capture stderr, verify format matches PRD example pattern)\nGREEN: Implement print_timing_summary, gate behind display.show_text\nVERIFY: cargo test && cargo clippy --all-targets -- -D warnings\n\n## Edge Cases\n- Empty stages (e.g., sync with no projects configured): print \"Sync complete in 0.0s\" with no stage lines\n- Very fast stages (<1ms): show \"0.0s\" not scientific notation\n- Stage names with varying lengths: dot padding keeps alignment readable","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-04T15:54:32.109882Z","created_by":"tayloreernisse","updated_at":"2026-02-04T17:32:52.558314Z","closed_at":"2026-02-04T17:32:52.558264Z","close_reason":"Added print_timing_summary with per-stage breakdown (name, elapsed, items, errors, rate limits), nested sub-stage support, gated behind metrics Option","compaction_level":0,"original_size":0,"labels":["observability"],"dependencies":[{"issue_id":"bd-1b0n","depends_on_id":"bd-1zj6","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1b0n","depends_on_id":"bd-3er","type":"parent-child","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]}
{"id":"bd-1b50","title":"Update existing tests for new ScoringConfig fields","description":"## Background\nThe existing test test_expert_scoring_weights_are_configurable (who.rs:3551-3574) constructs a ScoringConfig with only the original 3 fields. After bd-2w1p adds 8 new fields, this test will not compile without ..Default::default().\n\n## Approach\nFind the test at who.rs:3551-3574. The flipped config construction at line 3567:\n```rust\nlet flipped = ScoringConfig {\n author_weight: 5,\n reviewer_weight: 30,\n note_bonus: 1,\n};\n```\nChange to:\n```rust\nlet flipped = ScoringConfig {\n author_weight: 5,\n reviewer_weight: 30,\n note_bonus: 1,\n ..Default::default()\n};\n```\n\nAlso check default_scoring() helper at line 2451 — it calls ScoringConfig::default() which already works.\n\n### Important: Scope boundary\nThis bead ONLY handles ScoringConfig struct literal changes. The query_expert() function signature change (7 params -> 10 params) happens in bd-13q8 (Layer 3), which is responsible for updating all test callsites at that time.\n\n### Why existing assertions do not break:\nAll test data is inserted with now_ms(). With as_of_ms also at ~now_ms(), elapsed ~0ms, decay ~1.0. So integer-rounded scores are identical to the flat-weight model.\n\n## Acceptance Criteria\n- [ ] cargo test passes with zero assertion changes to existing test values\n- [ ] test_expert_scoring_weights_are_configurable compiles and passes\n- [ ] All other existing who tests pass unchanged\n- [ ] No new test code needed — only ..Default::default() additions\n- [ ] cargo check --all-targets clean\n\n## Files\n- MODIFY: src/cli/commands/who.rs (ScoringConfig literal at line 3567)\n\n## TDD Loop\nN/A — mechanical change, no new tests.\nVERIFY: cargo check --all-targets && cargo test -p lore -- test_expert_scoring_weights_are_configurable\n\n## Edge Cases\n- Search for ALL ScoringConfig { ... } literals in test module — there may be more than the one at line 3567\n- The default_scoring() helper at line 2451 uses ScoringConfig::default() — no change needed","status":"closed","priority":3,"issue_type":"task","created_at":"2026-02-09T17:00:45.084472Z","created_by":"tayloreernisse","updated_at":"2026-02-12T20:43:04.409277Z","closed_at":"2026-02-12T20:43:04.409239Z","close_reason":"Implemented by time-decay swarm: 3 agents, 12 tasks, 621 tests passing, all quality gates green","compaction_level":0,"original_size":0,"labels":["scoring","test"],"dependencies":[{"issue_id":"bd-1b50","depends_on_id":"bd-2w1p","type":"blocks","created_at":"2026-02-18T17:42:00Z","created_by":"import"}]} {"id":"bd-1b50","title":"Update existing tests for new ScoringConfig fields","description":"## Background\nThe existing test test_expert_scoring_weights_are_configurable (who.rs:3551-3574) constructs a ScoringConfig with only the original 3 fields. After bd-2w1p adds 8 new fields, this test will not compile without ..Default::default().\n\n## Approach\nFind the test at who.rs:3551-3574. The flipped config construction at line 3567:\n```rust\nlet flipped = ScoringConfig {\n author_weight: 5,\n reviewer_weight: 30,\n note_bonus: 1,\n};\n```\nChange to:\n```rust\nlet flipped = ScoringConfig {\n author_weight: 5,\n reviewer_weight: 30,\n note_bonus: 1,\n ..Default::default()\n};\n```\n\nAlso check default_scoring() helper at line 2451 — it calls ScoringConfig::default() which already works.\n\n### Important: Scope boundary\nThis bead ONLY handles ScoringConfig struct literal changes. The query_expert() function signature change (7 params -> 10 params) happens in bd-13q8 (Layer 3), which is responsible for updating all test callsites at that time.\n\n### Why existing assertions do not break:\nAll test data is inserted with now_ms(). With as_of_ms also at ~now_ms(), elapsed ~0ms, decay ~1.0. So integer-rounded scores are identical to the flat-weight model.\n\n## Acceptance Criteria\n- [ ] cargo test passes with zero assertion changes to existing test values\n- [ ] test_expert_scoring_weights_are_configurable compiles and passes\n- [ ] All other existing who tests pass unchanged\n- [ ] No new test code needed — only ..Default::default() additions\n- [ ] cargo check --all-targets clean\n\n## Files\n- MODIFY: src/cli/commands/who.rs (ScoringConfig literal at line 3567)\n\n## TDD Loop\nN/A — mechanical change, no new tests.\nVERIFY: cargo check --all-targets && cargo test -p lore -- test_expert_scoring_weights_are_configurable\n\n## Edge Cases\n- Search for ALL ScoringConfig { ... } literals in test module — there may be more than the one at line 3567\n- The default_scoring() helper at line 2451 uses ScoringConfig::default() — no change needed","status":"closed","priority":3,"issue_type":"task","created_at":"2026-02-09T17:00:45.084472Z","created_by":"tayloreernisse","updated_at":"2026-02-12T20:43:04.409277Z","closed_at":"2026-02-12T20:43:04.409239Z","close_reason":"Implemented by time-decay swarm: 3 agents, 12 tasks, 621 tests passing, all quality gates green","compaction_level":0,"original_size":0,"labels":["scoring","test"],"dependencies":[{"issue_id":"bd-1b50","depends_on_id":"bd-2w1p","type":"blocks","created_at":"2026-02-18T17:42:00Z","created_by":"import"}]}
{"id":"bd-1b6k","title":"Epic: TUI Phase 5.5 — Reliability Test Pack","description":"## Background\nPhase 5.5 is a comprehensive reliability test suite covering race conditions, stress tests, property-based testing, and deterministic clock verification. These tests ensure the TUI is robust under adverse conditions (rapid input, concurrent writes, resize storms, backpressure).\n\n## Acceptance Criteria\n- [ ] Stale response drop tests pass\n- [ ] Sync cancel/resume tests pass\n- [ ] SQLITE_BUSY retry tests pass\n- [ ] Resize storm + rapid keypress tests pass without panic\n- [ ] Property tests for navigation invariants pass\n- [ ] Performance benchmark fixtures (S/M/L tiers) pass SLOs\n- [ ] Event fuzz tests: 10k traces with zero invariant violations\n- [ ] Deterministic clock/render tests produce identical output\n- [ ] 30-minute soak test: no panic, no deadlock, memory growth < 5%\n- [ ] Concurrent pagination/write race tests: no duplicate/skipped rows\n- [ ] Query cancellation race tests: no cross-task bleed, no stuck loading","status":"open","priority":1,"issue_type":"epic","created_at":"2026-02-12T17:04:04.486702Z","created_by":"tayloreernisse","updated_at":"2026-02-12T18:11:51.508682Z","compaction_level":0,"original_size":0,"labels":["TUI"],"dependencies":[{"issue_id":"bd-1b6k","depends_on_id":"bd-3t6r","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]} {"id":"bd-1b6k","title":"Epic: TUI Phase 5.5 — Reliability Test Pack","description":"## Background\nPhase 5.5 is a comprehensive reliability test suite covering race conditions, stress tests, property-based testing, and deterministic clock verification. These tests ensure the TUI is robust under adverse conditions (rapid input, concurrent writes, resize storms, backpressure).\n\n## Acceptance Criteria\n- [ ] Stale response drop tests pass\n- [ ] Sync cancel/resume tests pass\n- [ ] SQLITE_BUSY retry tests pass\n- [ ] Resize storm + rapid keypress tests pass without panic\n- [ ] Property tests for navigation invariants pass\n- [ ] Performance benchmark fixtures (S/M/L tiers) pass SLOs\n- [ ] Event fuzz tests: 10k traces with zero invariant violations\n- [ ] Deterministic clock/render tests produce identical output\n- [ ] 30-minute soak test: no panic, no deadlock, memory growth < 5%\n- [ ] Concurrent pagination/write race tests: no duplicate/skipped rows\n- [ ] Query cancellation race tests: no cross-task bleed, no stuck loading","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-02-12T17:04:04.486702Z","created_by":"tayloreernisse","updated_at":"2026-02-19T12:49:54.360492Z","closed_at":"2026-02-19T12:49:54.360447Z","close_reason":"All 6 child beads closed: race conditions (16 tests), stress/fuzz (9), nav properties (10), perf benchmarks (14), soak (7), pagination races (7) = 63 reliability tests total","compaction_level":0,"original_size":0,"labels":["TUI"],"dependencies":[{"issue_id":"bd-1b6k","depends_on_id":"bd-3t6r","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]}
{"id":"bd-1b91","title":"CLI: show issue status display (human + robot)","description":"## Background\nOnce status data is in the DB, lore show issue needs to display it. Human view shows colored status text; robot view includes all 5 fields as JSON.\n\n## Approach\nAdd 5 fields to the IssueRow/IssueDetail/IssueDetailJson structs. Extend both find_issue SQL queries. Add status display line after State in human view. New style_with_hex() helper converts hex color to ANSI 256.\n\n## Files\n- src/cli/commands/show.rs\n\n## Implementation\n\nAdd to IssueRow (private struct):\n status_name: Option<String>, status_category: Option<String>,\n status_color: Option<String>, status_icon_name: Option<String>,\n status_synced_at: Option<i64>\n\nUpdate BOTH find_issue SQL queries (with and without project filter) SELECT list — add after existing columns:\n i.status_name, i.status_category, i.status_color, i.status_icon_name, i.status_synced_at\nColumn indices: status_name=12, status_category=13, status_color=14, status_icon_name=15, status_synced_at=16\n\nRow mapping (after milestone_title: row.get(11)?):\n status_name: row.get(12)?, ..., status_synced_at: row.get(16)?\n\nAdd to IssueDetail (public struct) — same 5 fields\nAdd to IssueDetailJson — same 5 fields\nAdd to From<&IssueDetail> for IssueDetailJson — clone/copy fields\n\nHuman display in print_show_issue (after State line):\n if let Some(status) = &issue.status_name {\n let display = match &issue.status_category {\n Some(cat) => format!(\"{status} ({})\", cat.to_ascii_lowercase()),\n None => status.clone(),\n };\n println!(\"Status: {}\", style_with_hex(&display, issue.status_color.as_deref()));\n }\n\nNew helper:\n fn style_with_hex<'a>(text: &'a str, hex: Option<&str>) -> console::StyledObject<&'a str>\n Parses 6-char hex (strips #), converts via ansi256_from_rgb, falls back to unstyled\n\n## Acceptance Criteria\n- [ ] Human: \"Status: In progress (in_progress)\" shown after State line\n- [ ] Status colored by hex -> ANSI 256\n- [ ] Status line omitted when status_name IS NULL\n- [ ] Robot: all 5 fields present as null when no status\n- [ ] Robot: status_synced_at is integer (ms epoch) or null\n- [ ] Both SQL queries updated (with and without project filter)\n- [ ] cargo check --all-targets passes\n\n## TDD Loop\nRED: No new dedicated test file — verify via cargo test show (existing tests should still pass)\nGREEN: Add fields, SQL columns, display logic\nVERIFY: cargo test show && cargo check --all-targets\n\n## Edge Cases\n- Two separate SQL strings in find_issue — BOTH must be updated identically\n- Column indices are positional — count carefully from 0\n- style_with_hex: hex.len() == 6 check after trimming # prefix\n- Invalid hex -> fall back to unstyled (no panic)\n- NULL hex color -> fall back to unstyled\n- clippy: use let-chain for combined if conditions (if hex.len() == 6 && let (...) = ...)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-11T06:42:16.215984Z","created_by":"tayloreernisse","updated_at":"2026-02-11T07:21:33.420281Z","closed_at":"2026-02-11T07:21:33.420236Z","close_reason":"Implemented by agent swarm — all quality gates pass (595 tests, 0 failures)","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-1b91","depends_on_id":"bd-2y79","type":"parent-child","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1b91","depends_on_id":"bd-3dum","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]} {"id":"bd-1b91","title":"CLI: show issue status display (human + robot)","description":"## Background\nOnce status data is in the DB, lore show issue needs to display it. Human view shows colored status text; robot view includes all 5 fields as JSON.\n\n## Approach\nAdd 5 fields to the IssueRow/IssueDetail/IssueDetailJson structs. Extend both find_issue SQL queries. Add status display line after State in human view. New style_with_hex() helper converts hex color to ANSI 256.\n\n## Files\n- src/cli/commands/show.rs\n\n## Implementation\n\nAdd to IssueRow (private struct):\n status_name: Option<String>, status_category: Option<String>,\n status_color: Option<String>, status_icon_name: Option<String>,\n status_synced_at: Option<i64>\n\nUpdate BOTH find_issue SQL queries (with and without project filter) SELECT list — add after existing columns:\n i.status_name, i.status_category, i.status_color, i.status_icon_name, i.status_synced_at\nColumn indices: status_name=12, status_category=13, status_color=14, status_icon_name=15, status_synced_at=16\n\nRow mapping (after milestone_title: row.get(11)?):\n status_name: row.get(12)?, ..., status_synced_at: row.get(16)?\n\nAdd to IssueDetail (public struct) — same 5 fields\nAdd to IssueDetailJson — same 5 fields\nAdd to From<&IssueDetail> for IssueDetailJson — clone/copy fields\n\nHuman display in print_show_issue (after State line):\n if let Some(status) = &issue.status_name {\n let display = match &issue.status_category {\n Some(cat) => format!(\"{status} ({})\", cat.to_ascii_lowercase()),\n None => status.clone(),\n };\n println!(\"Status: {}\", style_with_hex(&display, issue.status_color.as_deref()));\n }\n\nNew helper:\n fn style_with_hex<'a>(text: &'a str, hex: Option<&str>) -> console::StyledObject<&'a str>\n Parses 6-char hex (strips #), converts via ansi256_from_rgb, falls back to unstyled\n\n## Acceptance Criteria\n- [ ] Human: \"Status: In progress (in_progress)\" shown after State line\n- [ ] Status colored by hex -> ANSI 256\n- [ ] Status line omitted when status_name IS NULL\n- [ ] Robot: all 5 fields present as null when no status\n- [ ] Robot: status_synced_at is integer (ms epoch) or null\n- [ ] Both SQL queries updated (with and without project filter)\n- [ ] cargo check --all-targets passes\n\n## TDD Loop\nRED: No new dedicated test file — verify via cargo test show (existing tests should still pass)\nGREEN: Add fields, SQL columns, display logic\nVERIFY: cargo test show && cargo check --all-targets\n\n## Edge Cases\n- Two separate SQL strings in find_issue — BOTH must be updated identically\n- Column indices are positional — count carefully from 0\n- style_with_hex: hex.len() == 6 check after trimming # prefix\n- Invalid hex -> fall back to unstyled (no panic)\n- NULL hex color -> fall back to unstyled\n- clippy: use let-chain for combined if conditions (if hex.len() == 6 && let (...) = ...)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-02-11T06:42:16.215984Z","created_by":"tayloreernisse","updated_at":"2026-02-11T07:21:33.420281Z","closed_at":"2026-02-11T07:21:33.420236Z","close_reason":"Implemented by agent swarm — all quality gates pass (595 tests, 0 failures)","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-1b91","depends_on_id":"bd-2y79","type":"parent-child","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1b91","depends_on_id":"bd-3dum","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]}
{"id":"bd-1cb","title":"[CP0] gi doctor command - health checks","description":"## Background\n\ndoctor is the primary diagnostic command. It checks all system components and reports their status. Supports JSON output for scripting and CI integration. Must degrade gracefully - warn about optional components (Ollama) without failing.\n\nReference: docs/prd/checkpoint-0.md section \"gi doctor\"\n\n## Approach\n\n**src/cli/commands/doctor.ts:**\n\nPerforms 5 checks:\n1. **Config**: Load and validate config file\n2. **Database**: Open DB, verify pragmas, check schema version\n3. **GitLab**: Auth with token, verify connectivity\n4. **Projects**: Count configured vs resolved in DB\n5. **Ollama**: Ping embedding endpoint (optional - warn if unavailable)\n\n**DoctorResult interface:**\n```typescript\ninterface DoctorResult {\n success: boolean; // All required checks passed\n checks: {\n config: { status: 'ok' | 'error'; path?: string; error?: string };\n database: { status: 'ok' | 'error'; path?: string; schemaVersion?: number; error?: string };\n gitlab: { status: 'ok' | 'error'; url?: string; username?: string; error?: string };\n projects: { status: 'ok' | 'error'; configured?: number; resolved?: number; error?: string };\n ollama: { status: 'ok' | 'warning' | 'error'; url?: string; model?: string; error?: string };\n };\n}\n```\n\n**Human-readable output (default):**\n```\ngi doctor\n\n Config ✓ Loaded from ~/.config/gi/config.json\n Database ✓ ~/.local/share/gi/data.db (schema v1)\n GitLab ✓ https://gitlab.example.com (authenticated as @johndoe)\n Projects ✓ 2 configured, 2 resolved\n Ollama ⚠ Not running (semantic search unavailable)\n\nStatus: Ready (lexical search available, semantic search requires Ollama)\n```\n\n**JSON output (--json flag):**\nOutputs DoctorResult as JSON to stdout\n\n## Acceptance Criteria\n\n- [ ] Config check: shows path and validation status\n- [ ] Database check: shows path, schema version, pragma verification\n- [ ] GitLab check: shows URL and authenticated username\n- [ ] Projects check: shows configured count and resolved count\n- [ ] Ollama check: warns if not running, doesn't fail overall\n- [ ] success=true only if config, database, gitlab, projects all ok\n- [ ] --json outputs valid JSON matching DoctorResult interface\n- [ ] Exit 0 if success=true, exit 1 if any required check fails\n- [ ] Colors and symbols in human output (✓, ⚠, ✗)\n\n## Files\n\nCREATE:\n- src/cli/commands/doctor.ts\n- src/types/doctor.ts (DoctorResult interface)\n\n## TDD Loop\n\nN/A - diagnostic command, verify with manual testing:\n\n```bash\n# All good\ngi doctor\n\n# JSON output\ngi doctor --json | jq .\n\n# With missing Ollama\n# (just don't run Ollama - should show warning)\n\n# With bad config\nmv ~/.config/gi/config.json ~/.config/gi/config.json.bak\ngi doctor # should show config error\n```\n\n## Edge Cases\n\n- Ollama timeout should be short (2s) - don't block on slow network\n- Ollama 404 (wrong model) vs connection refused (not running)\n- Database file exists but wrong schema version\n- Projects in config but not in database (init not run)\n- Token valid for user but project access revoked","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-24T16:09:51.435540Z","created_by":"tayloreernisse","updated_at":"2026-01-25T03:30:24.921206Z","closed_at":"2026-01-25T03:30:24.921041Z","close_reason":"done","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-1cb","depends_on_id":"bd-13b","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-1l1","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-3ng","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-epj","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]} {"id":"bd-1cb","title":"[CP0] gi doctor command - health checks","description":"## Background\n\ndoctor is the primary diagnostic command. It checks all system components and reports their status. Supports JSON output for scripting and CI integration. Must degrade gracefully - warn about optional components (Ollama) without failing.\n\nReference: docs/prd/checkpoint-0.md section \"gi doctor\"\n\n## Approach\n\n**src/cli/commands/doctor.ts:**\n\nPerforms 5 checks:\n1. **Config**: Load and validate config file\n2. **Database**: Open DB, verify pragmas, check schema version\n3. **GitLab**: Auth with token, verify connectivity\n4. **Projects**: Count configured vs resolved in DB\n5. **Ollama**: Ping embedding endpoint (optional - warn if unavailable)\n\n**DoctorResult interface:**\n```typescript\ninterface DoctorResult {\n success: boolean; // All required checks passed\n checks: {\n config: { status: 'ok' | 'error'; path?: string; error?: string };\n database: { status: 'ok' | 'error'; path?: string; schemaVersion?: number; error?: string };\n gitlab: { status: 'ok' | 'error'; url?: string; username?: string; error?: string };\n projects: { status: 'ok' | 'error'; configured?: number; resolved?: number; error?: string };\n ollama: { status: 'ok' | 'warning' | 'error'; url?: string; model?: string; error?: string };\n };\n}\n```\n\n**Human-readable output (default):**\n```\ngi doctor\n\n Config ✓ Loaded from ~/.config/gi/config.json\n Database ✓ ~/.local/share/gi/data.db (schema v1)\n GitLab ✓ https://gitlab.example.com (authenticated as @johndoe)\n Projects ✓ 2 configured, 2 resolved\n Ollama ⚠ Not running (semantic search unavailable)\n\nStatus: Ready (lexical search available, semantic search requires Ollama)\n```\n\n**JSON output (--json flag):**\nOutputs DoctorResult as JSON to stdout\n\n## Acceptance Criteria\n\n- [ ] Config check: shows path and validation status\n- [ ] Database check: shows path, schema version, pragma verification\n- [ ] GitLab check: shows URL and authenticated username\n- [ ] Projects check: shows configured count and resolved count\n- [ ] Ollama check: warns if not running, doesn't fail overall\n- [ ] success=true only if config, database, gitlab, projects all ok\n- [ ] --json outputs valid JSON matching DoctorResult interface\n- [ ] Exit 0 if success=true, exit 1 if any required check fails\n- [ ] Colors and symbols in human output (✓, ⚠, ✗)\n\n## Files\n\nCREATE:\n- src/cli/commands/doctor.ts\n- src/types/doctor.ts (DoctorResult interface)\n\n## TDD Loop\n\nN/A - diagnostic command, verify with manual testing:\n\n```bash\n# All good\ngi doctor\n\n# JSON output\ngi doctor --json | jq .\n\n# With missing Ollama\n# (just don't run Ollama - should show warning)\n\n# With bad config\nmv ~/.config/gi/config.json ~/.config/gi/config.json.bak\ngi doctor # should show config error\n```\n\n## Edge Cases\n\n- Ollama timeout should be short (2s) - don't block on slow network\n- Ollama 404 (wrong model) vs connection refused (not running)\n- Database file exists but wrong schema version\n- Projects in config but not in database (init not run)\n- Token valid for user but project access revoked","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-24T16:09:51.435540Z","created_by":"tayloreernisse","updated_at":"2026-01-25T03:30:24.921206Z","closed_at":"2026-01-25T03:30:24.921041Z","close_reason":"done","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-1cb","depends_on_id":"bd-13b","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-1l1","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-3ng","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"},{"issue_id":"bd-1cb","depends_on_id":"bd-epj","type":"blocks","created_at":"2026-02-12T19:34:39Z","created_by":"import"}]}
{"id":"bd-1cj0","title":"Epic: TUI Phase 0 — Toolchain Gate","description":"## Background\nPhase 0 is the hard gate for the TUI implementation. It validates that FrankenTUI (nightly Rust) can coexist with the stable lore workspace, that core infrastructure types compile and pass basic tests, and that terminal compatibility meets the bar. If Phase 0 fails, we evaluate alternatives before proceeding.\n\n## Acceptance Criteria\n- [ ] crates/lore-tui/ scaffold exists with Cargo.toml, rust-toolchain.toml, main.rs, lib.rs\n- [ ] cargo +stable check --workspace --all-targets passes for root workspace (lore-tui EXCLUDED)\n- [ ] cargo +nightly check --manifest-path crates/lore-tui/Cargo.toml --all-targets passes\n- [ ] FrankenTUI Model trait skeleton compiles and renders a hello-world frame\n- [ ] DbManager, Clock, safety, and core type modules compile with tests\n- [ ] Terminal compat smoke test passes in iTerm2 and tmux\n\n## Scope\nAll Phase 0 tasks are blockers for Phase 1 (Foundation).","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-02-12T16:52:50.687401Z","created_by":"tayloreernisse","updated_at":"2026-02-12T20:07:27.208080Z","closed_at":"2026-02-12T20:07:27.208030Z","close_reason":"All Phase 0 children complete: scaffold (bd-3ddw), core types (bd-c9gk), clock (bd-2lg6), safety (bd-3ir1), DbManager (bd-2kop), theme (bd-5ofk), FrankenTUI integration (bd-2emv). 68 tests, quality gate green.","compaction_level":0,"original_size":0,"labels":["TUI"]} {"id":"bd-1cj0","title":"Epic: TUI Phase 0 — Toolchain Gate","description":"## Background\nPhase 0 is the hard gate for the TUI implementation. It validates that FrankenTUI (nightly Rust) can coexist with the stable lore workspace, that core infrastructure types compile and pass basic tests, and that terminal compatibility meets the bar. If Phase 0 fails, we evaluate alternatives before proceeding.\n\n## Acceptance Criteria\n- [ ] crates/lore-tui/ scaffold exists with Cargo.toml, rust-toolchain.toml, main.rs, lib.rs\n- [ ] cargo +stable check --workspace --all-targets passes for root workspace (lore-tui EXCLUDED)\n- [ ] cargo +nightly check --manifest-path crates/lore-tui/Cargo.toml --all-targets passes\n- [ ] FrankenTUI Model trait skeleton compiles and renders a hello-world frame\n- [ ] DbManager, Clock, safety, and core type modules compile with tests\n- [ ] Terminal compat smoke test passes in iTerm2 and tmux\n\n## Scope\nAll Phase 0 tasks are blockers for Phase 1 (Foundation).","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-02-12T16:52:50.687401Z","created_by":"tayloreernisse","updated_at":"2026-02-12T20:07:27.208080Z","closed_at":"2026-02-12T20:07:27.208030Z","close_reason":"All Phase 0 children complete: scaffold (bd-3ddw), core types (bd-c9gk), clock (bd-2lg6), safety (bd-3ir1), DbManager (bd-2kop), theme (bd-5ofk), FrankenTUI integration (bd-2emv). 68 tests, quality gate green.","compaction_level":0,"original_size":0,"labels":["TUI"]}

View File

@@ -1 +1 @@
bd-14hv bd-1b6k