|
| 1 | +# Code Smell Remediation Implementation Plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| 4 | +
|
| 5 | +**Goal:** Reduce the highest-maintenance code smells in Grapha without changing user-visible behavior, starting with the broken Swift index-store config and then splitting the largest multi-responsibility modules into focused units. |
| 6 | + |
| 7 | +**Architecture:** Fix correctness first by replacing the dead process-global index-store toggle with explicit configuration flow through the extraction pipeline. Then perform bounded structural refactors in three areas: Swift tree-sitter enrichment, CLI orchestration, and SQLite persistence. Each phase should preserve behavior with targeted tests before any further decomposition. |
| 8 | + |
| 9 | +**Tech Stack:** Rust workspace, clap, rusqlite, rayon, existing `grapha_core` plugin/extraction pipeline, existing integration/unit tests with `assert_cmd` |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## File Structure |
| 14 | + |
| 15 | +| File | Responsibility | |
| 16 | +|------|----------------| |
| 17 | +| Modify: `grapha/src/main.rs` | Current CLI entrypoint, pipeline orchestration, command dispatch, watch-mode server path | |
| 18 | +| Modify: `grapha/src/config.rs` | `swift.index_store` configuration source of truth | |
| 19 | +| Modify: `grapha-swift/src/lib.rs` | Swift extraction waterfall and index-store discovery | |
| 20 | +| Modify: `grapha-swift/src/treesitter.rs` | Current tree-sitter extractor plus all Swift enrichment passes | |
| 21 | +| Modify: `grapha/src/store/sqlite.rs` | Current SQLite schema, save/load, incremental sync, compatibility decode | |
| 22 | +| Create: `grapha-swift/src/treesitter/` | New focused Swift tree-sitter extraction/enrichment modules | |
| 23 | +| Create: `grapha/src/app/` or `grapha/src/commands/` | Extracted CLI orchestration and command handlers | |
| 24 | +| Create: `grapha/src/store/sqlite/` | Extracted SQLite schema/read/write helpers | |
| 25 | +| Modify: `grapha/tests/integration.rs` | CLI behavior regression coverage | |
| 26 | +| Modify: `grapha-swift/tests/*.rs` | Swift extraction/config regression coverage | |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +### Task 1: Fix the dead Swift index-store toggle |
| 31 | + |
| 32 | +**Files:** |
| 33 | +- Modify: `grapha/src/main.rs` |
| 34 | +- Modify: `grapha/src/config.rs` |
| 35 | +- Modify: `grapha-swift/src/lib.rs` |
| 36 | +- Modify: `grapha-core` extraction context files if pipeline options must be carried through shared context types |
| 37 | +- Test: existing Swift extraction tests or new targeted regression coverage in `grapha-swift/tests/` |
| 38 | + |
| 39 | +- [ ] Add a failing regression test that proves `[swift].index_store = false` prevents index-store extraction and falls back to SwiftSyntax/tree-sitter behavior. |
| 40 | +- [ ] Remove the `unsafe { std::env::set_var("GRAPHA_SKIP_INDEX_STORE", "1") }` branch from [main.rs](/Users/wendell/Developer/oops-rs/grapha/grapha/src/main.rs#L484) and replace it with explicit pipeline configuration. |
| 41 | +- [ ] Thread an `index_store_enabled` flag through the pipeline into `grapha_swift::extract_swift(...)` so the behavior is controlled by typed inputs rather than process-global state. |
| 42 | +- [ ] Update `grapha-swift/src/lib.rs` so `extract_swift` only initializes/uses index store when the flag is enabled. |
| 43 | +- [ ] Run targeted Swift tests, then `cargo test -p grapha-swift` and `cargo test -p grapha -- integration`. |
| 44 | +- [ ] Commit the fix separately before any structural refactor work. |
| 45 | + |
| 46 | +### Task 2: Split Swift tree-sitter extraction from enrichment passes |
| 47 | + |
| 48 | +**Files:** |
| 49 | +- Modify: `grapha-swift/src/treesitter.rs` |
| 50 | +- Create: `grapha-swift/src/treesitter/mod.rs` |
| 51 | +- Create: `grapha-swift/src/treesitter/extract.rs` |
| 52 | +- Create: `grapha-swift/src/treesitter/doc_comments.rs` |
| 53 | +- Create: `grapha-swift/src/treesitter/swiftui.rs` |
| 54 | +- Create: `grapha-swift/src/treesitter/localization.rs` |
| 55 | +- Create: `grapha-swift/src/treesitter/assets.rs` |
| 56 | +- Create: `grapha-swift/src/treesitter/common.rs` if shared span/index helpers remain cross-cutting |
| 57 | +- Test: existing Swift tree-sitter tests plus new module-focused unit tests |
| 58 | + |
| 59 | +- [ ] Freeze behavior first with targeted tests around doc-comment enrichment, SwiftUI structure enrichment, localization metadata, and asset reference tagging. |
| 60 | +- [ ] Keep a thin public facade at `grapha-swift/src/treesitter.rs` or `mod.rs` that re-exports the current public API so call sites stay stable during the split. |
| 61 | +- [ ] Move generic parser/shared types (`parse_swift`, `EnrichmentContext`, span helpers, dedup helpers) into `common`/`extract` modules. |
| 62 | +- [ ] Move each enrichment concern into its own file with the smallest possible public surface. |
| 63 | +- [ ] Eliminate duplicated low-level helpers such as line/byte indexing by consolidating them in one shared helper module. |
| 64 | +- [ ] Run `cargo test -p grapha-swift` after each extraction to keep the split behaviorally neutral. |
| 65 | +- [ ] Commit the module split once tests pass without changing CLI output. |
| 66 | + |
| 67 | +### Task 3: Extract CLI orchestration out of `main.rs` |
| 68 | + |
| 69 | +**Files:** |
| 70 | +- Modify: `grapha/src/main.rs` |
| 71 | +- Create: `grapha/src/app/mod.rs` or `grapha/src/commands/mod.rs` |
| 72 | +- Create: `grapha/src/app/pipeline.rs` |
| 73 | +- Create: `grapha/src/app/index.rs` |
| 74 | +- Create: `grapha/src/app/query.rs` |
| 75 | +- Create: `grapha/src/app/serve.rs` |
| 76 | +- Create: `grapha/src/cli.rs` if clap types are split from runtime behavior |
| 77 | +- Test: `grapha/tests/integration.rs` |
| 78 | + |
| 79 | +- [ ] Preserve the existing command-line contract with a smoke test matrix for `analyze`, `index`, `symbol`, `flow`, `l10n`, `asset`, `repo`, and `serve --mcp`. |
| 80 | +- [ ] Move `run_pipeline` and indexing orchestration out of `main.rs` into a dedicated runtime module. |
| 81 | +- [ ] Move command-specific handlers (`handle_symbol_command`, `handle_flow_command`, `handle_l10n_command`, `handle_asset_command`, `handle_repo_command`) into one or more command modules grouped by behavior rather than by output format. |
| 82 | +- [ ] Reduce `main()` to parsing CLI args, building render options, and dispatching into extracted runtime functions. |
| 83 | +- [ ] Keep watch-mode logic out of the CLI parsing layer by isolating it behind a `serve`/`watch` runtime module. |
| 84 | +- [ ] Run `cargo test -p grapha -- integration` and a manual `cargo run -p grapha -- --help` sanity check. |
| 85 | +- [ ] Commit this as a pure structure change with no query semantics changes. |
| 86 | + |
| 87 | +### Task 4: Split SQLite persistence into schema, write, and read paths |
| 88 | + |
| 89 | +**Files:** |
| 90 | +- Modify: `grapha/src/store/sqlite.rs` |
| 91 | +- Create: `grapha/src/store/sqlite/mod.rs` |
| 92 | +- Create: `grapha/src/store/sqlite/schema.rs` |
| 93 | +- Create: `grapha/src/store/sqlite/write.rs` |
| 94 | +- Create: `grapha/src/store/sqlite/read.rs` |
| 95 | +- Create: `grapha/src/store/sqlite/compat.rs` |
| 96 | +- Test: move or keep existing `#[cfg(test)]` coverage adjacent to new modules |
| 97 | + |
| 98 | +- [ ] Lock in behavior with focused tests for full save/load, incremental sync, `load_filtered`, and legacy schema compatibility. |
| 99 | +- [ ] Extract schema/version constants and table creation helpers into `schema.rs`. |
| 100 | +- [ ] Extract full/incremental write paths into `write.rs`, keeping `SqliteStore` as the stable facade type. |
| 101 | +- [ ] Extract all load/decode logic into `read.rs` and isolate schema-version-specific edge decoding into `compat.rs`. |
| 102 | +- [ ] Replace string-built filtering where possible with parameterized SQL or tightly-scoped helper builders so SQL assembly is explicit and testable. |
| 103 | +- [ ] Keep `load_filtered` semantics stable for the localization fast path used in [main.rs:825](/Users/wendell/Developer/oops-rs/grapha/grapha/src/main.rs#L825). |
| 104 | +- [ ] Run `cargo test -p grapha sqlite_store` and then full `cargo test`. |
| 105 | +- [ ] Commit after the persistence tests and integration tests both pass. |
| 106 | + |
| 107 | +### Task 5: Re-run smell analysis and tighten local thresholds |
| 108 | + |
| 109 | +**Files:** |
| 110 | +- Modify: `grapha/src/query/smells.rs` only if threshold tuning or exclusions are needed |
| 111 | +- Modify: relevant tests if smell output changes |
| 112 | +- Use: generated `.grapha/` index for local verification |
| 113 | + |
| 114 | +- [ ] Re-index the repo with `cargo run -p grapha -- index .`. |
| 115 | +- [ ] Run `cargo run -p grapha -- repo smells -p .` and compare the before/after warning set. |
| 116 | +- [ ] Confirm the original hotspots are reduced: dead config path removed, `treesitter.rs` split, `main.rs` slimmed down, `sqlite.rs` split. |
| 117 | +- [ ] Only adjust smell thresholds if the remaining warnings are demonstrably false positives after the refactors. |
| 118 | +- [ ] Capture the remaining warnings in the final PR description so future cleanup has an explicit baseline. |
| 119 | + |
| 120 | +### Task 6: Full verification and integration checkpoint |
| 121 | + |
| 122 | +**Files:** |
| 123 | +- No new code expected unless verification exposes regressions |
| 124 | + |
| 125 | +- [ ] Run `cargo fmt -- --check`. |
| 126 | +- [ ] Run `cargo clippy --all-targets --all-features`. |
| 127 | +- [ ] Run `cargo test`. |
| 128 | +- [ ] Run one real-world CLI smoke pass: `cargo run -p grapha -- index .` followed by `cargo run -p grapha -- repo smells -p .`. |
| 129 | +- [ ] If all checks pass, prepare either a cleanup PR or continue with any remaining smell-specific follow-up in a new plan rather than extending this refactor indefinitely. |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +## Notes |
| 134 | + |
| 135 | +- The highest-priority fix is Task 1 because it is a correctness issue, not just style. |
| 136 | +- Tasks 2 through 4 should be reviewed as structural refactors. Avoid mixing new feature behavior into those commits. |
| 137 | +- If the tree-sitter or SQLite splits reveal hidden coupling that would require semantic rewrites, stop and create a follow-up plan rather than forcing the decomposition in one pass. |
0 commit comments