Skip to content

Refactor resolution flow and align docs#16

Merged
Nek-12 merged 1 commit intomainfrom
refactor/resolution-docs
Jan 25, 2026
Merged

Refactor resolution flow and align docs#16
Nek-12 merged 1 commit intomainfrom
refactor/resolution-docs

Conversation

@Nek-12
Copy link
Member

@Nek-12 Nek-12 commented Jan 24, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added new CLI flags: --scope, --config, --targets, --subproject, --offline, --refresh, --buildscript, and --include-builds for enhanced filtering and build configuration.
    • Updated ksrc open command syntax to accept file IDs in addition to file paths.
  • Documentation

    • Updated CLI documentation with new flag descriptions.
    • Removed stack overview documentation.
  • Tests

    • Added test coverage for included builds deduplication and output parsing.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

📝 Walkthrough

Walkthrough

This PR refactors the resolution pipeline by extracting orchestration logic from the Gradle layer into a new resolution service. The top-level Gradle Resolve function is removed, with its multi-pass logic (root, buildSrc, included builds) relocated to internal/gradle/traversal.go. A new internal/resolution/service.go package encapsulates cache fallback, attempt merging, and filtering logic. CLI helpers now delegate to this service, and search components adopt a strategy pattern with caching. Documentation reflects new CLI flags and architectural decisions.

Changes

Cohort / File(s) Summary
Documentation Updates
docs/cli-api.md, docs/decisions.md
Added new CLI flags (--scope, --config, --targets, --subproject, --offline, --refresh, --buildscript, --include-builds); updated ksrc open signature from <path> to <file-id|path>; removed --max-results option. New performance note on resolution orchestration split.
Documentation Removal
docs/stack.md
Deleted overview of ksrc stack (40 lines covering Go version, frameworks, dependencies, internal structure).
CLI Flags Layer
internal/cli/flags.go
Added ToRequest() method to convert ResolveFlags into resolution.Request with all flag fields and parameters (dep, applyFilters, allowCacheFallback).
CLI Helpers Refactoring
internal/cli/helpers.go
Replaced Gradle-based resolveSources with delegation to resolution.Service.ResolveSources; updated return type to resolution.ResolveMeta; updated helper function signatures (noSourcesHintForFlags, projectHint, metaHasConfig, emitDiagnostics) to use new meta type; removed 13 helper functions (resolveAttempt, buildResolveAttempts, formatAttemptConfigs, mergeSources, gradleFailureWarnings, etc.).
Gradle Resolution Reorganization
internal/gradle/gradle.go
Removed top-level Resolve orchestration function (140 lines) that handled root, buildSrc, and included-builds passes; now relies solely on resolveOnce single-pass logic.
Gradle Traversal Logic
internal/gradle/traversal.go
New file introducing hierarchical resolution pipeline via SingleResolver interface and Resolve function; orchestrates root resolution, buildSrc, and nested included-builds with deduplication, verbose diagnostics, and result merging.
Gradle Test Updates
internal/gradle/gradle_test.go
Added TestResolveIncludedBuildsDedupesAndBfs to validate included-builds deduplication and breadth-first traversal; introduced stubResolver mock type for test support.
Resolution Service
internal/resolution/service.go
New package with Service, Request, ResolveMeta, and Result types; implements ResolveSources orchestration (multi-attempt resolution, cache fallback, result merging, filtering, verbose diagnostics); includes 9 helper functions for attempt building, filtering, deduplication, and Gradle error handling.
Search Strategy Refactoring
internal/search/rg.go
Introduced strategy pattern via selectStrategy and searchStrategy struct; unified parsing via parseRgOutput with coordinate mapping; added zipSupportCache singleton for caching ZIP probe results; added supportsZipSearch and resetZipSupportCacheForTests functions.
Search Test Updates
internal/search/rg_test.go
Added TestParseRgOutputMapsFileIDs to validate coordinate mapping in RG output parsing; added cache reset calls in existing tests for isolation.

Sequence Diagram

sequenceDiagram
    actor CLI as CLI Command
    participant Flags as CLI Flags
    participant Service as resolution.Service
    participant Traversal as gradle.Traversal
    participant Gradle as Gradle Invocation
    participant Cache as Cache Store

    CLI->>Flags: Parse flags → ResolveFlags
    CLI->>Service: flags.ToRequest() → Request
    Service->>Service: buildResolveAttempts(req)
    loop For each attempt
        Service->>Traversal: gradle.Resolve(ctx, opts)
        Traversal->>Gradle: resolveOnce(root)
        Gradle-->>Traversal: Result
        alt buildSrc enabled
            Traversal->>Gradle: resolveOnce(buildSrc)
            Gradle-->>Traversal: Result
        end
        alt included builds enabled
            loop For each included build (BFS)
                Traversal->>Gradle: resolveOnce(includedBuild)
                Gradle-->>Traversal: Result
            end
        end
        Traversal-->>Service: ResolveResult
        alt Gradle success
            Service->>Service: mergeSources/mergeDeps
        else Gradle failure
            Service->>Cache: cacheFallbackSources(req)
            Cache-->>Service: cached sources/coords
        end
    end
    Service->>Service: applyFilters (if enabled)
    Service-->>CLI: Result {Sources, Deps, Meta}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: refactoring the resolution flow (moving orchestration from gradle.go to traversal.go and introducing resolution.Service) and aligning documentation (updating CLI docs and decisions docs).

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/resolution-docs

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
Contributor

@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

🤖 Fix all issues with AI agents
In `@docs/cli-api.md`:
- Around line 78-84: The Usage block under the "ksrc open <file-id|path>"
section should be converted to a proper subheading and the fenced code block
should include a language; replace the bold "Usage" line with a markdown heading
(e.g., "#### Usage") and change the triple-backtick fence to specify the
language as "text" so the example becomes a fenced block with ```text followed
by the command "ksrc open <file-id|path> [flags]" then closing ```; update the
section around the "ksrc open <file-id|path>" heading accordingly.

In `@internal/resolution/service.go`:
- Around line 250-299: The bug is that isExactToken treats empty strings as
exact which makes hasExactSelector and cacheFallbackSources take the
exact-selector branch when coord.Version is absent; change isExactToken (used by
hasExactSelector) so that after trimming it returns false for empty values
(i.e., treat empty as non-exact), keep the existing wildcard/charset/comma
checks otherwise; this will cause hasExactSelector to return false for missing
versions and let cacheFallbackSources follow the fallback path instead of
calling resolve.FindCachedSources with an empty version.

In `@internal/search/rg.go`:
- Around line 115-131: The code in parseRgOutput currently uses
strings.TrimSpace(line), which removes all leading/trailing whitespace and may
hide real leading spaces in ripgrep output; change this to only strip line
terminators (e.g. use strings.TrimRight(line, "\r\n")) so leading/trailing
spaces inside the line are preserved, keeping parseRgLine/mapping behavior
intact for function parseRgOutput and preserving exact match text.

Comment on lines +78 to 84
### `ksrc open <file-id|path>`
Open a file in `$PAGER` (defaults to `less -R`).

**Usage**
```
ksrc open <path> [flags]
ksrc open <file-id|path> [flags]
```
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdownlint MD036/MD040 in the ksrc open usage block.

Use a heading instead of bold text and specify a language for the fence.

📝 Suggested fix
-**Usage**
-```
-ksrc open <file-id|path> [flags]
-```
+#### Usage
+```text
+ksrc open <file-id|path> [flags]
+```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### `ksrc open <file-id|path>`
Open a file in `$PAGER` (defaults to `less -R`).
**Usage**
```
ksrc open <path> [flags]
ksrc open <file-id|path> [flags]
```
### `ksrc open <file-id|path>`
Open a file in `$PAGER` (defaults to `less -R`).
#### Usage
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

81-81: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


82-82: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@docs/cli-api.md` around lines 78 - 84, The Usage block under the "ksrc open
<file-id|path>" section should be converted to a proper subheading and the
fenced code block should include a language; replace the bold "Usage" line with
a markdown heading (e.g., "#### Usage") and change the triple-backtick fence to
specify the language as "text" so the example becomes a fenced block with
```text followed by the command "ksrc open <file-id|path> [flags]" then closing
```; update the section around the "ksrc open <file-id|path>" heading
accordingly.

Comment on lines +250 to +299
func cacheFallbackSources(req Request, dep string, applyFilters bool) ([]resolve.SourceJar, []resolve.Coord, error) {
var sources []resolve.SourceJar
var err error

if dep != "" {
coord, err := resolve.ParseCoord(dep)
if err != nil {
return nil, nil, err
}
sources, err = resolve.FindCachedSources(coord.Group, coord.Artifact, coord.Version)
if err != nil {
return nil, nil, err
}
} else if req.All || !hasExactSelector(req) {
sources, err = resolve.FindAllCachedSources()
if err != nil {
return nil, nil, err
}
} else {
coord, _ := resolve.SelectorToCoord(req.Module, req.Group, req.Artifact, req.Version)
sources, err = resolve.FindCachedSources(coord.Group, coord.Artifact, coord.Version)
if err != nil {
return nil, nil, err
}
}

if applyFilters {
sources = resolve.FilterSources(sources, req.Module, req.Group, req.Artifact, req.Version)
}
return sources, collectCoords(sources), nil
}

func hasExactSelector(req Request) bool {
coord, ok := resolve.SelectorToCoord(req.Module, req.Group, req.Artifact, req.Version)
if !ok {
return false
}
return isExactToken(coord.Group) && isExactToken(coord.Artifact) && isExactToken(coord.Version)
}

func isExactToken(value string) bool {
value = strings.TrimSpace(value)
if value == "" {
return true
}
if strings.ContainsAny(value, "*?[]") {
return false
}
return !strings.Contains(value, ",")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the function definitions for selector and cache operations
rg -n "func SelectorToCoord" --type go
echo "---"
rg -n "func FindCachedSources" --type go
echo "---"
rg -n "func FindAllCachedSources" --type go
echo "---"
# Also search for how empty versions are handled elsewhere
rg -n "Version.*=.*\"\"" --type go -A 2 -B 2

Repository: respawn-app/ksrc

Length of output: 2295


🏁 Script executed:

#!/bin/bash
# Read the selector and cache implementations
cat -n internal/resolve/selector.go
echo "=== SEPARATOR ==="
cat -n internal/resolve/cache.go

Repository: respawn-app/ksrc

Length of output: 4716


🏁 Script executed:

#!/bin/bash
# Verify the exact behavior of isExactToken with empty string
cat -n internal/resolution/service.go | sed -n '260,270p'

Repository: respawn-app/ksrc

Length of output: 508


Incorrect handling of empty version: should not be treated as "exact."

isExactToken returns true for empty strings, causing hasExactSelector to incorrectly return true when coord.Version is empty. This triggers the exact selector branch (case 3), which calls FindCachedSources with an empty version instead of falling back to FindAllCachedSources. While FindCachedSources internally recovers by calling HighestCachedVersion, the control flow is semantically incorrect—empty version represents an absent version and should be treated as non-exact.

Suggested guard
 func hasExactSelector(req Request) bool {
 	coord, ok := resolve.SelectorToCoord(req.Module, req.Group, req.Artifact, req.Version)
 	if !ok {
 		return false
 	}
+	if strings.TrimSpace(coord.Version) == "" {
+		return false
+	}
 	return isExactToken(coord.Group) && isExactToken(coord.Artifact) && isExactToken(coord.Version)
 }

This ensures that absent (empty) versions explicitly trigger the fallback path, aligning with the principle of preferring project-resolved dependency versions and falling back to cache-latest only when the version is absent.

🤖 Prompt for AI Agents
In `@internal/resolution/service.go` around lines 250 - 299, The bug is that
isExactToken treats empty strings as exact which makes hasExactSelector and
cacheFallbackSources take the exact-selector branch when coord.Version is
absent; change isExactToken (used by hasExactSelector) so that after trimming it
returns false for empty values (i.e., treat empty as non-exact), keep the
existing wildcard/charset/comma checks otherwise; this will cause
hasExactSelector to return false for missing versions and let
cacheFallbackSources follow the fallback path instead of calling
resolve.FindCachedSources with an empty version.

@Nek-12 Nek-12 merged commit 15e7533 into main Jan 25, 2026
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 31, 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