Skip to content

feat: show circular dependency warning in context bomb#18

Merged
greynewell merged 1 commit intomainfrom
claude/issue-12-20260227-0013
Feb 27, 2026
Merged

feat: show circular dependency warning in context bomb#18
greynewell merged 1 commit intomainfrom
claude/issue-12-20260227-0013

Conversation

@greynewell
Copy link
Contributor

@greynewell greynewell commented Feb 27, 2026

Adds circular dependency detection via /v1/graphs/circular-dependencies. Renders a blockquote warning when cycles exist. Cycle count is stored in Stats.CircularDependencyCycles so cache hits serve it with zero extra API calls.

Closes #12

Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Reports now include circular dependency counts when detected; analysis is collected automatically without interrupting processing.
    • Circular dependency counts are shown in compact/summary headers and considered when trimming long summaries.
    • Summary output adds a Tech stack line (external dependencies) when present.
    • Domains/subdomains are rendered as detailed lists; codebase summary shows Files and Functions counts.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Walkthrough

Adds fetching of circular-dependency data alongside project graphs: a helper wraps graph retrieval, polls a circular-dependency endpoint, merges cycle counts into graph.Stats, templates render the cycle count, and the API client gains GetCircularDependencies plus related types and a Stats.CircularDependencyCycles field.

Changes

Cohort / File(s) Summary
CLI handlers
cmd/run.go, cmd/pregen.go
Replace direct apiClient.GetGraph(...) calls with fetchGraphWithCircularDeps(...); unchanged error & cache paths but now attempt to attach circular-cycle count to graph.Stats.
API client & types
internal/api/client.go
Add CircularDependencyCycle, CircularDependencyResponse types, Stats.CircularDependencyCycles field, and GetCircularDependencies(ctx, projectName, repoZip) with polling, status handling, and parsing.
Template rendering / truncation
internal/template/render.go
Include circular-dependency cycles in header rendering and token-budget truncation; update truncateToTokenBudget signature to accept circularCycles and conditionally emit a cycles warning line.

Sequence Diagram

sequenceDiagram
    participant Handler as Handler
    participant Helper as fetchGraphWithCircularDeps
    participant Client as API Client
    participant GraphEP as GetGraph Endpoint
    participant CircularEP as CircularDeps Endpoint
    participant Cache as Cache

    Handler->>Helper: fetchGraphWithCircularDeps(ctx, client, projectName, repoZip, logFn)
    Helper->>Client: GetGraph(ctx, projectName, repoZip)
    Client->>GraphEP: submit & poll for graph
    GraphEP-->>Client: Graph data
    Client-->>Helper: Graph
    Helper->>Client: GetCircularDependencies(ctx, projectName, repoZip)
    Client->>CircularEP: submit & poll for cycles
    CircularEP-->>Client: CircularDependencyResponse / error
    Client-->>Helper: Cycles data (or error)
    Helper->>Helper: merge cycles -> graph.Stats.CircularDependencyCycles
    Helper-->>Handler: enhanced graph
    Handler->>Cache: store graph (with cycles)
    Handler->>Handler: render template (includes cycle line if present)
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🔄 Circles hidden in import threads,
The graph counts whispers codebase sheds,
A tiny warning, polite and neat,
Cached and ready — no surprise to meet. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding circular dependency detection and warnings to the context bomb UI.
Linked Issues check ✅ Passed The code implements all requirements from issue #12: detects circular dependencies via a new API endpoint, stores cycle counts in Stats, and renders warning blocks.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing circular dependency detection and warning display—no unrelated modifications detected.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/issue-12-20260227-0013

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

Copy link

@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

🧹 Nitpick comments (2)
internal/api/client.go (2)

361-363: Preserve backend error messages in non-success responses.

The generic API error %d drops useful server context, which makes incidents harder to debug.

🛠️ Suggested error handling parity with GetGraph
 		case http.StatusOK, http.StatusAccepted:
 			// Continue to parse
 		default:
-			return nil, fmt.Errorf("API error %d", resp.StatusCode)
+			var errResp struct {
+				Message string `json:"message"`
+				Error   string `json:"error"`
+			}
+			_ = json.Unmarshal(respBody, &errResp)
+			msg := errResp.Message
+			if msg == "" {
+				msg = errResp.Error
+			}
+			if msg == "" {
+				msg = string(respBody)
+			}
+			return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, msg)
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/client.go` around lines 361 - 363, The default error branch in
the response handling currently returns a generic error ("API error %d"); update
this to preserve the backend error message similar to GetGraph by reading the
response body (e.g., via resp.Body) and including its contents in the returned
error (combine a short context like "API error <status>" with the backend
message). Ensure you close or drain resp.Body and handle read errors gracefully
so the function (the switch/default in internal/api/client.go) returns an error
containing the server-provided text rather than only the status code.

293-296: Docstring and runtime behavior differ for the “no cycles” case.

Right now the function only returns nil, nil for unavailable endpoints; successful "no cycles" responses return a non-nil empty payload. Either behavior is fine, but the comment should match what the code does.

✏️ Suggested doc fix
-// and returns the list of detected import cycles. Returns nil, nil if the endpoint
-// is unavailable or returns no cycles — callers should treat this as "no data".
+// and returns the list of detected import cycles. Returns nil, nil if the endpoint
+// is unavailable. If available but no cycles are found, returns an empty response.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/client.go` around lines 293 - 296, Update the docstring for
GetCircularDependencies to match its runtime behavior: state that the function
returns a non-nil (empty) CircularDependencyResponse when the endpoint
successfully reports no cycles, and only returns nil, nil when the endpoint is
unavailable or cannot be reached; reference the GetCircularDependencies method
name and ensure the comment clarifies the distinction between "no cycles" (empty
payload) and "endpoint unavailable" (nil response).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/template/render.go`:
- Around line 17-19: The circular dependency warning currently only appears in
the full template branch and is lost when output is shortened by
truncateToTokenBudget; update the truncation path so the cycle signal is
preserved by either (A) having truncateToTokenBudget return/preserve a boolean
or string like hasCircularDependency (or the original
.Graph.Stats.CircularDependencyCycles count) alongside the truncated content, or
(B) inject a precomputed field (e.g. .HasCircularDependency or
.CircularDependencyCount) into the template data before calling
truncateToTokenBudget; then update the template check (currently using
.Graph.Stats.CircularDependencyCycles) to also consider the preserved field so
the warning is rendered even when truncation occurs. Ensure to adjust callers of
truncateToTokenBudget and the template rendering code that references
Graph.Stats.CircularDependencyCycles to read the new preserved field.

---

Nitpick comments:
In `@internal/api/client.go`:
- Around line 361-363: The default error branch in the response handling
currently returns a generic error ("API error %d"); update this to preserve the
backend error message similar to GetGraph by reading the response body (e.g.,
via resp.Body) and including its contents in the returned error (combine a short
context like "API error <status>" with the backend message). Ensure you close or
drain resp.Body and handle read errors gracefully so the function (the
switch/default in internal/api/client.go) returns an error containing the
server-provided text rather than only the status code.
- Around line 293-296: Update the docstring for GetCircularDependencies to match
its runtime behavior: state that the function returns a non-nil (empty)
CircularDependencyResponse when the endpoint successfully reports no cycles, and
only returns nil, nil when the endpoint is unavailable or cannot be reached;
reference the GetCircularDependencies method name and ensure the comment
clarifies the distinction between "no cycles" (empty payload) and "endpoint
unavailable" (nil response).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a0d1ed and dfc32a0.

📒 Files selected for processing (4)
  • cmd/pregen.go
  • cmd/run.go
  • internal/api/client.go
  • internal/template/render.go

@greynewell
Copy link
Contributor Author

@claude fix all comments and merge conflicts and get this merged

@claude
Copy link
Contributor

claude bot commented Feb 27, 2026

Claude finished @greynewell's task in 6m 49s —— View job


Fixing PR Review Comments + Merge Conflicts ✓

What was done:

  • Read relevant files for context
  • Rewrite internal/api/client.go — merged new data structures from main (Subdomain struct, ExternalDeps, []string Languages) with circular dep types/method + review fixes:
    • Fixed generic "API error %d" → preserves backend error message (parity with GetGraph)
    • Fixed docstring: distinguishes nil, nil (endpoint unavailable) from empty response (no cycles)
  • Rewrite internal/template/render.go — merged new template from main (ExternalDeps tech stack, new Subdomain rendering) with:
    • Circular dep warning block preserved
    • Major fix: truncateToTokenBudget now accepts circularCycles int and injects the warning into truncated output — cycle count is no longer lost for large repos
  • cmd/run.go and cmd/pregen.go were already correct (used fetchGraphWithCircularDeps)
  • Build verified (go build ./... passes)
  • Pushed as commit 78be48f

To merge: The PR is ready. You'll need to approve and merge it via the GitHub UI (I cannot merge PRs directly due to GitHub App permissions).
| Branch

@claude claude bot force-pushed the claude/issue-12-20260227-0013 branch from 78be48f to 2de0fb1 Compare February 27, 2026 00:51
Copy link

@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

🧹 Nitpick comments (2)
internal/api/client.go (1)

325-442: Consider extracting shared async polling into one helper.

GetCircularDependencies and GetGraph are almost the same control flow. A shared submit+poll helper would reduce drift (timeouts, status handling, retries, headers) and make fixes apply once.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/client.go` around lines 325 - 442, GetCircularDependencies and
GetGraph share nearly identical submit-and-poll logic; extract that flow into a
new helper (e.g., submitAndPollJob or postAndPollJob) that handles request
creation, idempotency key, multipart/body submission, response reading,
status-code handling (401, 402, 429, 404/405 defaulting to nil), JSON
unmarshalling into a JobStatus, polling retry logic (RetryAfter fallback,
deadline/maxPollAttempts), and error mapping so both GetCircularDependencies and
GetGraph call the helper with endpoint, payload, and a result-unmarshal
callback; update GetCircularDependencies to use the helper and keep specific
result unmarshalling into CircularDependencyResponse and status-to-error
handling for jobResp.Error, preserving existing logging via c.logFn and headers
(Content-Type, X-Api-Key, Accept, User-Agent, Idempotency-Key).
internal/template/render.go (1)

17-19: Consider centralizing cycle-warning text/pluralization.

The warning sentence logic now exists in two places (template + truncation builder). Small helper/shared formatter would keep wording and pluralization from drifting over time.

Also applies to: 119-125

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/template/render.go` around lines 17 - 19, Extract the warning text +
pluralization into a single shared helper (e.g., a function
FormatCircularDependencyWarning(count int) string or a template func
"circularDependencyWarning") and use that helper from the template (replace the
inline {{if ...}} block that references .Graph.Stats.CircularDependencyCycles)
and from the truncation builder code that currently repeats the same wording
(the logic around lines ~119-125). Ensure the helper accepts the cycle count,
returns the fully formatted sentence with correct singular/plural form, and is
exposed to templates via the template.FuncMap so both places call the same
formatter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/api/client.go`:
- Around line 436-438: The default branch of the job-status switch (where
c.logFn("[debug] unknown circular dep job status: %s", jobResp.Status) is
called) must treat unknown statuses as terminal or trigger a backoff instead of
immediately retrying; modify that default case in the job response handling
logic to log the unexpected status along with context, mark the job as
terminal/failed (or set a backoff flag/delay) so the caller will not immediately
POST again, and surface an error/return so retry logic can apply exponential
backoff rather than firing rapid requests.

---

Nitpick comments:
In `@internal/api/client.go`:
- Around line 325-442: GetCircularDependencies and GetGraph share nearly
identical submit-and-poll logic; extract that flow into a new helper (e.g.,
submitAndPollJob or postAndPollJob) that handles request creation, idempotency
key, multipart/body submission, response reading, status-code handling (401,
402, 429, 404/405 defaulting to nil), JSON unmarshalling into a JobStatus,
polling retry logic (RetryAfter fallback, deadline/maxPollAttempts), and error
mapping so both GetCircularDependencies and GetGraph call the helper with
endpoint, payload, and a result-unmarshal callback; update
GetCircularDependencies to use the helper and keep specific result unmarshalling
into CircularDependencyResponse and status-to-error handling for jobResp.Error,
preserving existing logging via c.logFn and headers (Content-Type, X-Api-Key,
Accept, User-Agent, Idempotency-Key).

In `@internal/template/render.go`:
- Around line 17-19: Extract the warning text + pluralization into a single
shared helper (e.g., a function FormatCircularDependencyWarning(count int)
string or a template func "circularDependencyWarning") and use that helper from
the template (replace the inline {{if ...}} block that references
.Graph.Stats.CircularDependencyCycles) and from the truncation builder code that
currently repeats the same wording (the logic around lines ~119-125). Ensure the
helper accepts the cycle count, returns the fully formatted sentence with
correct singular/plural form, and is exposed to templates via the
template.FuncMap so both places call the same formatter.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfc32a0 and 78be48f.

📒 Files selected for processing (2)
  • internal/api/client.go
  • internal/template/render.go

- Add CircularDependencyCycles field to Stats struct
- Add CircularDependencyCycle and CircularDependencyResponse types
- Add GetCircularDependencies API client method with proper error handling
- Add fetchGraphWithCircularDeps helper in cmd/ that calls both endpoints
- Render circular dependency warning in context bomb template
- Preserve warning in truncateToTokenBudget fallback path

Closes #12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greynewell greynewell force-pushed the claude/issue-12-20260227-0013 branch from 2de0fb1 to e64d4c3 Compare February 27, 2026 00:52
Copy link

@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.

🧹 Nitpick comments (1)
internal/template/render.go (1)

190-197: buildCriticalFilesSection is unused and can be removed.

This function is never called anywhere in the codebase. The truncateToTokenBudget function handles critical files inline with smart logic that respects the token budget — only including files that fit within the remaining tokens. Since buildCriticalFilesSection doesn't have that budget-aware logic, it looks like this helper was bypassed in favor of the inline approach.

Safe to delete to keep things clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/template/render.go` around lines 190 - 197, Remove the unused helper
function buildCriticalFilesSection from internal/template/render.go: locate the
function declaration for buildCriticalFilesSection and delete it (the
truncateToTokenBudget path already implements budget-aware critical file
handling), and run tests/grep to ensure no references remain so the codebase is
clean of dead code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/template/render.go`:
- Around line 190-197: Remove the unused helper function
buildCriticalFilesSection from internal/template/render.go: locate the function
declaration for buildCriticalFilesSection and delete it (the
truncateToTokenBudget path already implements budget-aware critical file
handling), and run tests/grep to ensure no references remain so the codebase is
clean of dead code.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78be48f and e64d4c3.

📒 Files selected for processing (4)
  • cmd/pregen.go
  • cmd/run.go
  • internal/api/client.go
  • internal/template/render.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/pregen.go

@greynewell greynewell merged commit 0b05772 into main Feb 27, 2026
9 of 10 checks passed
@greynewell greynewell deleted the claude/issue-12-20260227-0013 branch February 27, 2026 00:57
This was referenced Feb 27, 2026
This was referenced Feb 28, 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.

feat: show circular dependency warning in context bomb

1 participant