Skip to content

fix: address quality issues in ast-analysis, presentation, extractors, and cli#1790

Open
carlos-alm wants to merge 8 commits into
fix/titan-quality-fixes-resolver-graph-featuresfrom
fix/titan-quality-fixes-ast-presentation-cli
Open

fix: address quality issues in ast-analysis, presentation, extractors, and cli#1790
carlos-alm wants to merge 8 commits into
fix/titan-quality-fixes-resolver-graph-featuresfrom
fix/titan-quality-fixes-ast-presentation-cli

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

Address remaining quality issues from the Titan GAUNTLET audit across ast-analysis, presentation, extractors, cli, and scripts:

  • src/ast-analysis/visitor-utils.ts, cfg-conditionals.ts — dispatch table + processBranch dedupe
  • src/presentation/audit.ts — decompose renderAuditFunction, adopt typed AuditResult
  • Decompose highest-complexity functions across 7 extractor files (r, dart, groovy, csharp, elixir, scala, julia)
  • Split execute() in cli/commands/info.ts into printEngineInfo/printNativeVersionInfo/printBuildMetadata
  • Extract timeMedian helper in scripts/token-benchmark.ts

Titan Audit Context

Changes

  • src/ast-analysis/visitor-utils.ts, src/ast-analysis/visitors/cfg-conditionals.ts
  • src/features/audit.ts, src/presentation/audit.ts, src/types.ts
  • src/extractors/{r,dart,groovy,csharp,elixir,scala,julia}.ts
  • src/cli/commands/info.ts
  • scripts/token-benchmark.ts

Metrics Impact

Part of the quality-fix pass addressing GAUNTLET fail-level violations; see generated/titan/ report for full metrics.

Test plan

  • CI passes (lint + build + tests)
  • codegraph check --cycles --boundaries passes
  • No new functions above complexity thresholds

…ck acknowledged)

Impact: 48 functions changed, 9 affected
…nowledged)

Decomposes the highest-complexity functions across 7 independent
single-language extractor files (sync.json phase 26): r, dart, groovy,
csharp, elixir, scala, julia. Pure behavior-preserving decomposition —
no extraction-logic changes. Resolution benchmark precision/recall/
TP/FP/FN confirmed byte-for-byte identical to baseline for all 7
languages.

Impact: 22 functions changed, 29 affected
…/printBuildMetadata

docs check acknowledged — pure internal decomposition of CLI output
logic, no new features/languages/architecture changes; README/CLAUDE/
ROADMAP do not need updates.

Impact: 5 functions changed, 2 affected
Impact: 2 functions changed, 3 affected
@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR addresses quality issues from a GAUNTLET audit by extracting helpers, building dispatch tables, and adopting concrete types across ast-analysis, presentation, extractors, cli, and scripts. It is a purely structural refactor: no business logic changes are intended.

  • cfg-conditionals.ts: introduces processBranch to dedup 6+ inline make-block/add-edge/run-body/fallthrough sequences; renames nnrequireNode throughout CFG visitors.
  • presentation/audit.ts / features/audit.ts / types.ts: renderAuditFunction is decomposed into 6 focused helpers; AuditResult/AuditFunctionEntry/AuditHealthMetrics/ThresholdBreach are promoted to exported types with corrected field shapes (callees/callers were always SymbolRef[], not string[]).
  • Extractors: highest-complexity functions in r, dart, groovy, elixir, scala, julia, csharp are each split into one-responsibility helpers; groovy replaces a switch with a GROOVY_NODE_HANDLERS record; timeMedian is extracted in token-benchmark.ts.

Confidence Score: 5/5

Safe to merge — all changes are structural refactors with no business logic modifications; behavior is preserved throughout.

Every changed file is a mechanical extraction: helpers pulled out of long functions, dispatch tables replacing switch/if-else cascades, and type declarations promoted from inline any to concrete exported interfaces. The CFG branch processing, extractor AST walks, audit rendering, and CLI info output all produce the same results as before. The LANGTRACE_LANG rename in native-tracer.sh is a genuine correctness fix (avoids clobbering the POSIX locale). No new code paths, no new external calls, no schema or protocol changes.

No files require special attention. native-tracer.sh uses bash dynamic scoping for the shared maybe_* helpers, which is non-obvious but is explicitly documented in the new comments.

Important Files Changed

Filename Overview
src/ast-analysis/visitors/cfg-conditionals.ts Introduces processBranch helper, eliminating 6+ duplicated make-block/add-edge/run-body/fallthrough sequences; nnrequireNode rename throughout; logic is functionally identical to the original.
src/ast-analysis/visitor-utils.ts Replaces if/else-if cascade in resolveParamNode with a PARAM_NODE_HANDLERS dispatch table; extracts visitParamWorklistNode, pushParamWorklist, isShorthandPropPattern helpers; all referenced functions are hoisted declarations so module-init order is safe.
src/ast-analysis/visitors/cfg-shared.ts Renames nnrequireNode and parameter Sstate; purely cosmetic, no logic change.
src/extractors/groovy.ts Replaces switch dispatch in walkGroovyNode with GROOVY_NODE_HANDLERS record; all original case groups (including aliased node-type pairs) are preserved in the table.
src/extractors/dart.ts Extracts resolveDartSelectorMethodName and isDartFunctionApplyCall from handleDartSelector; both layout-A and layout-B selector resolution paths and the Function.apply dynamic-dispatch guard are preserved exactly.
src/extractors/julia.ts Extracts resolveJuliaStructHeadNames, collectJuliaStructFields, and collectJuliaSelectedImportParts; source mutation is correctly preserved via return-value threading in the selected-import helper.
src/types.ts Promotes ThresholdBreach, AuditHealthMetrics to exported interfaces; corrects AuditFunctionEntry field types (callees/callers → object arrays, healthAuditHealthMetrics, lineCount/riskScore → nullable); prior review confirmed the JSON output was always objects, not strings.
src/presentation/audit.ts Decomposes renderAuditFunction into six focused helpers; adopts AuditResult/AuditFunctionEntry types; any casts removed; prior double-cast comment addressed in previous review round.
src/features/audit.ts Adopts AuditResult, AuditFunctionEntry, AuditHealthMetrics, ThresholdBreach from types.ts; tightens ExplainResult/FileSymbol optional fields to non-optional per their documented runtime contract; unknown return types replaced with concrete types throughout.
src/cli/commands/info.ts Splits execute() into printEngineInfo, printNativeVersionInfo, printBuildMetadata; previously flagged loadNative()! non-null assertion is now replaced by a safe null-check on loadNative()'s return value inside printEngineInfo.
tests/benchmarks/resolution/tracer/native-tracer.sh Introduces inject_trace_calls + three maybe_* helpers to deduplicate Rust/Swift/Zig/Dart instrumentation loops; correctly renames LANGTRACE_LANG to avoid clobbering the POSIX locale variable; relies on bash dynamic scoping for shared locals, with the failure mode explicitly documented in comments.
scripts/token-benchmark.ts Extracts timeMedian helper, eliminating three identical timing loops; wraps synchronous fnDepsData/fnImpactData in async timeMedianawait on a non-Promise resolves immediately, so measurements remain correct.
tests/benchmarks/resolution/tracer/lua-tracer.lua Adds io.stderr:write for fixture errors so genuine Lua syntax failures surface instead of silently producing zero call edges.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph cfg-conditionals ["cfg-conditionals.ts (new shared helper)"]
        processBranch["processBranch(condBlock, joinBlock, S, branchKind, label, runBranchBody)"]
        B1[makeBlock] --> B2[addEdge condBlock→branchBlock]
        B2 --> B3[runBranchBody → branchEnd]
        B3 --> B4{branchEnd non-null?}
        B4 -->|yes| B5[addEdge branchEnd→joinBlock fallthrough]
        processBranch --> B1
    end

    subgraph callers ["Previously inlined 6+ times — now delegating"]
        processIf -->|true branch| processBranch
        processAlternative -->|else-if branch| processBranch
        processAlternative -->|else branch| processBranch
        processElifSiblings -->|elif true branch| processBranch
        processElifSiblings -->|elif else branch| processBranch
    end

    subgraph audit-presentation ["presentation/audit.ts decomposition"]
        renderAuditFunction --> renderFunctionHeader
        renderAuditFunction --> renderHealthMetrics
        renderAuditFunction --> renderThresholdBreaches
        renderAuditFunction --> renderImpactSection
        renderAuditFunction --> renderCallRefs_calls["renderCallRefs('Calls')"]
        renderAuditFunction --> renderCallRefs_calledby["renderCallRefs('Called by')"]
        renderAuditFunction --> renderRelatedTests
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph cfg-conditionals ["cfg-conditionals.ts (new shared helper)"]
        processBranch["processBranch(condBlock, joinBlock, S, branchKind, label, runBranchBody)"]
        B1[makeBlock] --> B2[addEdge condBlock→branchBlock]
        B2 --> B3[runBranchBody → branchEnd]
        B3 --> B4{branchEnd non-null?}
        B4 -->|yes| B5[addEdge branchEnd→joinBlock fallthrough]
        processBranch --> B1
    end

    subgraph callers ["Previously inlined 6+ times — now delegating"]
        processIf -->|true branch| processBranch
        processAlternative -->|else-if branch| processBranch
        processAlternative -->|else branch| processBranch
        processElifSiblings -->|elif true branch| processBranch
        processElifSiblings -->|elif else branch| processBranch
    end

    subgraph audit-presentation ["presentation/audit.ts decomposition"]
        renderAuditFunction --> renderFunctionHeader
        renderAuditFunction --> renderHealthMetrics
        renderAuditFunction --> renderThresholdBreaches
        renderAuditFunction --> renderImpactSection
        renderAuditFunction --> renderCallRefs_calls["renderCallRefs('Calls')"]
        renderAuditFunction --> renderCallRefs_calledby["renderCallRefs('Called by')"]
        renderAuditFunction --> renderRelatedTests
    end
Loading

Reviews (4): Last reviewed commit: "refactor: address warnings in benchmark ..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

120 functions changed98 callers affected across 26 files

  • timeMedian in scripts/token-benchmark.ts:272 (3 transitive callers)
  • runPerfBenchmarks in scripts/token-benchmark.ts:286 (2 transitive callers)
  • ParamNodeHandler.matches in src/ast-analysis/visitor-utils.ts:105 (0 transitive callers)
  • ParamNodeHandler.resolve in src/ast-analysis/visitor-utils.ts:106 (0 transitive callers)
  • resolveWrapperParam in src/ast-analysis/visitor-utils.ts:109 (0 transitive callers)
  • resolveDefaultParam in src/ast-analysis/visitor-utils.ts:114 (0 transitive callers)
  • resolveRestParam in src/ast-analysis/visitor-utils.ts:119 (0 transitive callers)
  • resolveObjectDestructParam in src/ast-analysis/visitor-utils.ts:128 (0 transitive callers)
  • resolveArrayDestructParam in src/ast-analysis/visitor-utils.ts:135 (0 transitive callers)
  • resolveParamNode in src/ast-analysis/visitor-utils.ts:180 (4 transitive callers)
  • isShorthandPropPattern in src/ast-analysis/visitor-utils.ts:219 (4 transitive callers)
  • pushParamWorklist in src/ast-analysis/visitor-utils.ts:228 (4 transitive callers)
  • visitParamWorklistNode in src/ast-analysis/visitor-utils.ts:236 (5 transitive callers)
  • extractParamNames in src/ast-analysis/visitor-utils.ts:259 (6 transitive callers)
  • processBranch in src/ast-analysis/visitors/cfg-conditionals.ts:22 (5 transitive callers)
  • processIf in src/ast-analysis/visitors/cfg-conditionals.ts:36 (4 transitive callers)
  • processAlternative in src/ast-analysis/visitors/cfg-conditionals.ts:71 (3 transitive callers)
  • processElifSiblings in src/ast-analysis/visitors/cfg-conditionals.ts:114 (4 transitive callers)
  • processSwitch in src/ast-analysis/visitors/cfg-conditionals.ts:169 (3 transitive callers)
  • extractCaseBody in src/ast-analysis/visitors/cfg-conditionals.ts:219 (3 transitive callers)

Comment thread src/presentation/audit.ts Outdated
const data: AuditResult = auditData(target, customDbPath, opts);

if (outputResult(data, null, opts)) return;
if (outputResult(data as unknown as Record<string, unknown>, null, opts)) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Double cast bypasses well-typed AuditResult

data as unknown as Record<string, unknown> is required only because outputResult takes a Record<string, unknown> parameter. Since AuditResult is now a concrete, exported interface, widening outputResult's signature to accept object would remove the escape hatch and let the type system verify callers. The cast is not a bug today, but it silently defeats the type improvements introduced elsewhere in this PR.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

@carlos-alm carlos-alm Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — removed the double cast entirely. Turns out it wasn't load-bearing: AuditResult assigns cleanly to outputResult's Record<string, any> parameter without any cast (verified with tsc --noEmit). Widening outputResult to accept object would be a good follow-up but touches ~20 call sites across the presentation layer that use the same as unknown as Record<string, unknown> pattern (queries-cli/impact.ts, path.ts, inspect.ts, overview.ts, triage.ts, owners.ts, exports.ts) — out of scope for this PR, which is limited to ast-analysis/presentation/extractors/cli. Filed #1803 for the repo-wide signature widening.

Comment thread src/types.ts
Comment on lines 1650 to 1656
levels: Record<number, ImpactLevelEntry[]>;
};
health: {
cognitive: number;
cyclomatic: number;
maxNesting: number;
maintainabilityIndex: number | null;
halstead: HalsteadMetrics | null;
loc: number;
sloc: number;
commentLines: number;
thresholdBreaches: string[];
};
riskScore: number;
complexityNotes: string[];
sideEffects: string[];
health: AuditHealthMetrics;
riskScore: number | null;
complexityNotes: string | null;
sideEffects: string | null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 AuditFunctionEntry field types corrected from incorrect prior declarations

callees/callers change from string[] to Array<{name, kind, file, line}>, and relatedTests from string[] to Array<{file: string}>. These are corrections — the actual runtime values were always objects — but any external consumer of codegraph audit --json that treated these as string arrays will silently break. Worth calling out in release notes if the JSON output is considered a public API.

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged, but verified this is not an actual behavior/output change. I traced callees/callers/relatedTests back through features/audit.ts history (e.g. commit 4a6787e) — they were always populated with SymbolRef[] and {file}[] objects at runtime; only the TS declaration was wrong. Since codegraph audit --json was already emitting objects (never strings), no external consumer that actually inspected the real output could have relied on the incorrect string-array shape, so no release-note callout is needed here — the JSON contract itself hasn't changed, just the type-checker's understanding of it.

@carlos-alm

Copy link
Copy Markdown
Contributor Author

Addressed Claude's/Greptile's review feedback:

  • src/cli/commands/info.ts:445 (non-null assertion on loadNative()!) — fixed. printNativeVersionInfo now takes native: NativeAddon directly instead of loadNative: () => NativeAddon | null; the caller (printEngineInfo) calls loadNative() once and only invokes printNativeVersionInfo if the result is non-null, so isNativeAvailable()/loadNative() divergence can no longer crash.
  • src/presentation/audit.ts:111 (double cast bypassing AuditResult) — fixed. Removed the as unknown as Record<string, unknown> cast entirely; AuditResult assigns cleanly to outputResult's Record<string, any> param without it (verified with tsc --noEmit). Filed follow-up: widen outputResult signature to accept typed objects instead of Record<string, any> #1803 to widen outputResult's signature repo-wide and drop the same redundant cast at ~20 other call sites — out of scope for this PR.
  • src/types.ts:1650-1656 (AuditFunctionEntry field-type corrections) — replied inline confirming this is a type-only correction with no runtime/JSON-output change (the fields were always objects at runtime), so no release-note callout is needed.

…nfo (#1790)

printNativeVersionInfo now takes a resolved NativeAddon directly instead of
re-invoking loadNative() and asserting it non-null. isNativeAvailable() and
loadNative() are two independent code paths; if they ever diverge the
assertion would crash instead of degrading gracefully.

Impact: 2 functions changed, 2 affected
AuditResult assigns cleanly to outputResult's Record<string, any> parameter
without a cast; the `as unknown as Record<string, unknown>` was dead weight
left over from before AuditResult was a concrete type.

Impact: 1 functions changed, 1 affected
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

…is naming (#1791)

* refactor: address warnings in benchmark tracer tooling

Split loader-hooks.mjs's instrumentSource (cognitive 34, cyclomatic 22)
into single-purpose matcher/tracker helpers. Extract the duplicated
line-scanning loop shared by native-tracer.sh's trace_rust/trace_swift/
trace_dart/trace_zig into a parameterized inject_trace_calls plus small
maybe_close_context/maybe_close_finally_scope/maybe_inject_declaration/
instrument_one_file helpers, and rename the LANG variable to TRACE_LANG
so it no longer clobbers the POSIX locale env var inherited by every
spawned compiler toolchain. Log lua-tracer.lua's swallowed pcall error
to stderr so a genuine fixture failure is visible instead of looking
like a silent successful trace.

Impact: 17 functions changed, 11 affected

* refactor: address warnings in ast-analysis and extractors/helpers naming (docs check acknowledged)

Impact: 12 functions changed, 36 affected

* fix: use compgen -G for glob expansion in inject_trace_calls (#1791)

Unquoted `for srcfile in $glob_pattern` word-splits before globbing, so
a TMP_DIR containing a space would silently skip every file instead of
instrumenting it. compgen -G expands the glob pattern as a single
argument, avoiding the split. Also documents the concrete failure mode
of calling a maybe_* helper outside the inject_trace_calls ->
instrument_one_file chain.

Impact: 1 functions changed, 5 affected
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