Skip to content

Refactor to reduce CRAP#164

Merged
aleyan merged 8 commits into
mainfrom
refactor
May 20, 2026
Merged

Refactor to reduce CRAP#164
aleyan merged 8 commits into
mainfrom
refactor

Conversation

@aleyan
Copy link
Copy Markdown
Owner

@aleyan aleyan commented May 20, 2026

Summary by CodeRabbit

  • Refactor

    • Unified how discovered task definitions are stored for more consistent, ordered retrieval.
    • Streamlined verbose task-list output to emit clear per-file status lines and relative paths when available.
  • Tests

    • Added unit and async tests covering discovery, listing, prompt rendering, and initialization behaviors.
  • Chores

    • Updated project plan checklist and regenerated the code-metrics baseline.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3eea38c6-0b2d-454e-9e30-2e06ee48e7ed

📥 Commits

Reviewing files that changed from the base of the PR and between a6f5adf and bb44624.

📒 Files selected for processing (2)
  • cargo_crap_baseline.json
  • src/types.rs

📝 Walkthrough

Walkthrough

Moves DiscoveredTaskDefinitions into src/types.rs with BTreeMap storage and insert/get APIs; updates discovery modules and support functions to use insert; rewrites many tests to use get_first/get_all; refactors list verbose reporting; adds tests and regenerates cargo_crap baseline and project plan checklist.

Changes

DiscoveredTaskDefinitions Migration

Layer / File(s) Summary
DiscoveredTaskDefinitions data structure refactoring
src/types.rs
DiscoveredTaskDefinitions replaces per-type Option<TaskDefinitionFile> fields with a private BTreeMap<TaskDefinitionType, Vec<TaskDefinitionFile>>; adds insert, get_first, get_all, and iter; extends ShadowType derives; removes DiscoveredTasks; adds unit tests.
Discovery implementations updated to use new insert API
src/task_discovery.rs (import), src/task_discovery/support.rs, src/task_discovery/npm.rs, src/task_discovery/python.rs, src/task_discovery/docker_compose.rs, src/task_discovery/github_actions.rs, src/task_discovery/gradle.rs, src/task_discovery/justfile.rs
Discovery modules and support.rs::set_definition now call discovered.definitions.insert(...) instead of assigning per-type fields; task_discovery.rs imports DiscoveredTaskDefinitions from src/types.rs.
Test suite updated to use get_first/get_all API
src/task_discovery.rs (test code)
Test assertions across Makefile, npm, Python, Turbo, Taskfile, Maven, GitHub Actions, Docker Compose, Travis CI, CMake, and Justfile discovery tests were rewritten to use discovered.definitions.get_first(&TaskDefinitionType::...) or get_first(...).is_some() where appropriate.
Verbose reporting refactored to iterate over grouped definitions
src/commands/list.rs
Verbose output now iterates over discovered.definitions and emits one status line per discovered file by matching TaskFileStatus. test_println! macro evaluates format_args! in tests. New unit test added for execute success using a CwdGuard.
Main, MCP, and prompt tests
src/main.rs, src/commands/mcp.rs, src/prompt.rs
main delegates to new async run_command helper and adds a tokio test; mcp gains a serialized Tokio test verifying init Cursor config creation using TestEnvGuard; prompt gains a TUI render unit test using TestBackend.
Cargo-crap metrics and project plan updates
cargo_crap_baseline.json, dev_docs/project_plan.md
Regenerated cargo-crap baseline reflecting updated line numbers and metrics; Phase 11 technical debt checklist entries DTKT-202/DTKT-203/DTKT-204 marked complete.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • aleyan/dela#160: Related refactor to task discovery storage and consumers.
  • aleyan/dela#159: Related work on definition metadata and consumption by listing/UI.
  • aleyan/dela#152: Prior refactor affecting DiscoveredTaskDefinitions map usage and list output.

Poem

🐰 I hopped through fields and found a tree,
Moved scattered leaves to order, neat and free.
Insert and get now guide each quest,
Tests all passed, the checklist blessed.
A little hop — the code sleeps peacefully.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the pull request, which is to reduce CRAP (Change Risk Anti-Pattern) metrics through comprehensive refactoring.
Docstring Coverage ✅ Passed Docstring coverage is 98.48% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/task_discovery/github_actions.rs (1)

106-112: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Record actual workflow files and statuses here.

Now that verbose reporting reads from definitions.files, inserting one synthetic Parsed entry for .github/workflows only when all_tasks is non-empty loses real source information. Repos that only have workflow.yml / custom/workflows/*.yml, or repos where every workflow fails to parse, end up with inaccurate or missing GitHub Actions definition reporting.

Possible fix
 fn discover_github_actions_tasks(
     dir: &Path,
     discovered: &mut DiscoveredTasks,
 ) -> anyhow::Result<()> {
     let mut workflow_files = Vec::new();
+    let workflows_parent = dir.join(".github").join("workflows");
 
-    let workflows_dir = dir.join(".github").join("workflows");
+    let workflows_dir = workflows_parent.clone();
     if workflows_dir.exists() && workflows_dir.is_dir() {
         match fs::read_dir(&workflows_dir) {
             Ok(entries) => {
@@
-    if workflow_files.is_empty() {
-        return Ok(());
-    }
+    if workflow_files.is_empty() {
+        discovered.definitions.insert(TaskDefinitionFile {
+            path: workflows_parent.clone(),
+            definition_type: TaskDefinitionType::GitHubActions,
+            status: TaskFileStatus::NotFound,
+        });
+        return Ok(());
+    }
 
     let mut all_tasks = Vec::new();
     let mut errors = Vec::new();
-    let workflows_parent = dir.join(".github").join("workflows");
 
     for file_path in workflow_files {
         match parse_github_actions(&file_path) {
             Ok(mut tasks) => {
+                discovered.definitions.insert(TaskDefinitionFile {
+                    path: file_path.clone(),
+                    definition_type: TaskDefinitionType::GitHubActions,
+                    status: TaskFileStatus::Parsed,
+                });
                 let source =
                     ComposedDefinitionSource::composed(workflows_parent.clone(), file_path);
                 for task in &mut tasks {
@@
-            Err(error) => errors.push(format!(
-                "Failed to parse workflow file {:?}: {}",
-                file_path, error
-            )),
+            Err(error) => {
+                discovered.definitions.insert(TaskDefinitionFile {
+                    path: file_path.clone(),
+                    definition_type: TaskDefinitionType::GitHubActions,
+                    status: TaskFileStatus::ParseError(error.to_string()),
+                });
+                errors.push(format!(
+                    "Failed to parse workflow file {:?}: {}",
+                    file_path, error
+                ));
+            }
         }
     }
 
@@
-    if !all_tasks.is_empty() {
-        discovered.definitions.insert(TaskDefinitionFile {
-            path: workflows_parent,
-            definition_type: TaskDefinitionType::GitHubActions,
-            status: TaskFileStatus::Parsed,
-        });
+    if !all_tasks.is_empty() {
         discovered.tasks.extend(all_tasks);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/task_discovery/github_actions.rs` around lines 106 - 112, Currently the
code inserts a single synthetic TaskDefinitionFile for workflows_parent when
all_tasks is non-empty, which loses per-file provenance and status; change this
to record each actual workflow file and its parse status instead: for each
workflow path found (instead of only using workflows_parent) create and insert a
TaskDefinitionFile with the real path, TaskDefinitionType::GitHubActions, and
the correct TaskFileStatus (Parsed for successfully parsed files, Failed or
whatever failure status you track for parse errors), and only extend
discovered.tasks with the parsed tasks (all_tasks) — update the logic around
all_tasks, discovered.definitions.insert, TaskDefinitionFile, workflows_parent,
and TaskFileStatus::Parsed to iterate per-file and preserve real file-level
statuses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cargo_crap_baseline.json`:
- Around line 8-11: The execute function in ./src/commands/list.rs has excessive
cyclomatic complexity and zero test coverage; refactor it by extracting smaller,
well-named helper functions (e.g., parse_list_args, collect_packages,
filter_packages, compute_package_metrics, and format_list_output) to move
independent match arms, loops, and conditional logic out of execute, reduce
branching inside execute to high-level orchestration, and then add unit tests
for each helper plus focused integration tests for execute (using mocks/stubs
for IO and external dependencies) to cover all branches and error paths so the
overall cyclomatic and CRAP metrics fall below the threshold.

In `@src/task_discovery.rs`:
- Line 18: Run rustfmt (e.g., cargo fmt) on the file to fix the formatting
issues flagged by CI; specifically ensure the use statement importing Task,
TaskDefinitionFile, and DiscoveredTaskDefinitions and any changed assertions are
formatted according to rustfmt rules so cargo fmt --all will pass (this will
resolve the failing cargo fmt --all -- --check CI step).

In `@src/types.rs`:
- Around line 259-260: The file src/types.rs is not formatted and is failing
cargo fmt --all -- --check; run rustfmt (or cargo fmt --all) to reformat
src/types.rs (fix spacing, alignment for struct/enum/impl/function declarations
within types.rs), stage the reformatted file and update the commit so CI passes;
ensure any local rustfmt configuration is honored and that cargo fmt --all --
--check succeeds before pushing.
- Around line 133-147: Make the internal multi-valued storage private and
provide explicit accessors: change the pub field files in
DiscoveredTaskDefinitions to private (remove pub), keep insert as-is, replace
the current get method with a get_first that returns Option<&TaskDefinitionFile>
if you still need the first item, and add two new public accessors such as pub
fn get_all(&self, definition_type: &TaskDefinitionType) ->
Option<&[TaskDefinitionFile]> (or Option<&Vec<TaskDefinitionFile>>) to return
all definitions for a given type and pub fn iter(&self) -> impl Iterator<Item =
(&TaskDefinitionType, &[TaskDefinitionFile])> (or a suitable concrete iterator)
to iterate entries; update callers of DiscoveredTaskDefinitions::get to use
get_first/get_all/iter as appropriate. Ensure function/type names referenced:
DiscoveredTaskDefinitions, files, insert, get (rename), get_all, iter.

---

Outside diff comments:
In `@src/task_discovery/github_actions.rs`:
- Around line 106-112: Currently the code inserts a single synthetic
TaskDefinitionFile for workflows_parent when all_tasks is non-empty, which loses
per-file provenance and status; change this to record each actual workflow file
and its parse status instead: for each workflow path found (instead of only
using workflows_parent) create and insert a TaskDefinitionFile with the real
path, TaskDefinitionType::GitHubActions, and the correct TaskFileStatus (Parsed
for successfully parsed files, Failed or whatever failure status you track for
parse errors), and only extend discovered.tasks with the parsed tasks
(all_tasks) — update the logic around all_tasks, discovered.definitions.insert,
TaskDefinitionFile, workflows_parent, and TaskFileStatus::Parsed to iterate
per-file and preserve real file-level statuses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8112844a-4e2b-40bb-9193-dee3ecec06ad

📥 Commits

Reviewing files that changed from the base of the PR and between 931ae02 and 2693937.

📒 Files selected for processing (12)
  • cargo_crap_baseline.json
  • dev_docs/project_plan.md
  • src/commands/list.rs
  • src/task_discovery.rs
  • src/task_discovery/docker_compose.rs
  • src/task_discovery/github_actions.rs
  • src/task_discovery/gradle.rs
  • src/task_discovery/justfile.rs
  • src/task_discovery/npm.rs
  • src/task_discovery/python.rs
  • src/task_discovery/support.rs
  • src/types.rs

Comment thread cargo_crap_baseline.json Outdated
Comment on lines +8 to +11
"line": 22,
"cyclomatic": 43.0,
"coverage": 0.0,
"crap": 380.0
"crap": 1892.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

High CRAP score remains after refactoring.

The execute function in ./src/commands/list.rs still has a CRAP score of 1892.0 with cyclomatic complexity of 43. While the PR refactored verbose reporting to reduce duplication, the core execute function remains highly complex with zero test coverage.

Consider breaking down the execute function into smaller helper functions and adding unit tests to reduce this risk metric below the typical threshold of 30.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cargo_crap_baseline.json` around lines 8 - 11, The execute function in
./src/commands/list.rs has excessive cyclomatic complexity and zero test
coverage; refactor it by extracting smaller, well-named helper functions (e.g.,
parse_list_args, collect_packages, filter_packages, compute_package_metrics, and
format_list_output) to move independent match arms, loops, and conditional logic
out of execute, reduce branching inside execute to high-level orchestration, and
then add unit tests for each helper plus focused integration tests for execute
(using mocks/stubs for IO and external dependencies) to cover all branches and
error paths so the overall cyclomatic and CRAP metrics fall below the threshold.

Comment thread src/task_discovery.rs Outdated
Comment thread src/types.rs
Comment thread src/types.rs Outdated
Comment on lines 259 to 260


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run rustfmt on this file before merge.

cargo fmt --all -- --check is already failing here, so this file is currently blocking CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types.rs` around lines 259 - 260, The file src/types.rs is not formatted
and is failing cargo fmt --all -- --check; run rustfmt (or cargo fmt --all) to
reformat src/types.rs (fix spacing, alignment for struct/enum/impl/function
declarations within types.rs), stage the reformatted file and update the commit
so CI passes; ensure any local rustfmt configuration is honored and that cargo
fmt --all -- --check succeeds before pushing.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/list.rs`:
- Around line 36-38: The current verbose output uses file.path.file_name()
(assigned to file_name) which drops parent directories and causes
duplicate-looking entries; change the logic that computes file_name so it prints
a relative path instead (e.g., compute a path relative to the workspace/current
working directory via Path::strip_prefix or similar and fall back to the full
path if strip_prefix fails) and use that relative-path string wherever file_name
is used in the verbose definition output (references: discovered.definitions,
file.path, and the file_name binding).
- Around line 951-967: The test currently saves original_dir and only resets cwd
after execute/assert succeed, which leaks the temp_dir on panic; replace that
manual restore with a RAII guard that captures original_dir when created (e.g.,
CwdGuard or scopeguard) and restores it in Drop so the working directory is
always reset even if execute(...) or assert! panics; locate the setup around
original_dir, temp_dir and the execute(true, "never") call in this test and
instantiate the guard immediately after changing to temp_dir.path() so Drop will
call std::env::set_current_dir(original_dir) unconditionally.

In `@src/commands/mcp.rs`:
- Around line 491-530: Test cleanup (restoring CWD and HOME) can be skipped if
the test panics or an assertion fails; wrap the original_dir and old_home
capture in an RAII guard so restoration always runs. Implement a small guard
struct (e.g., TestEnvGuard) that stores old_dir: Option<PathBuf> and old_home:
Option<String> and implements Drop to restore with std::env::set_current_dir /
set_var / remove_var, then create an instance at the start of
test_execute_init_cursor before changing the current dir/HOME and remove the
manual restore block at the end; keep assertions (assert!(result.is_ok()) and
expected_path.exists()) after calling execute so the guard still runs on panic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a0ced326-ed51-47e0-babb-71107dd36721

📥 Commits

Reviewing files that changed from the base of the PR and between 2693937 and 9430061.

📒 Files selected for processing (7)
  • cargo_crap_baseline.json
  • src/commands/list.rs
  • src/commands/mcp.rs
  • src/main.rs
  • src/prompt.rs
  • src/task_discovery.rs
  • src/types.rs

Comment thread src/commands/list.rs Outdated
Comment thread src/commands/list.rs Outdated
Comment thread src/commands/mcp.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cargo_crap_baseline.json`:
- Around line 1205-1212: DiscoveredTaskDefinitions::get_all currently has no
direct test coverage; add a focused unit test that constructs a
DiscoveredTaskDefinitions instance with multiple task definitions (including at
least two definitions sharing the same task key/name and some unique ones),
calls DiscoveredTaskDefinitions::get_all, and asserts the returned collection
shape and contents (count, grouping of multiple definitions under the same key,
and that each definition's fields match the inputs). Place the test in the
appropriate test module (e.g., a #[cfg(test)] mod tests in the same file or a
unit test under tests/) and include cases for empty and multi-definition
retrieval to lock the grouping contract for get_all.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eea4c2ca-c0f3-43e3-9721-7279fe7b4172

📥 Commits

Reviewing files that changed from the base of the PR and between 9430061 and a6f5adf.

📒 Files selected for processing (3)
  • cargo_crap_baseline.json
  • src/commands/list.rs
  • src/commands/mcp.rs

Comment thread cargo_crap_baseline.json Outdated
@aleyan aleyan merged commit 6c9debc into main May 20, 2026
9 checks passed
This was referenced May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant