KInspector is a Kotlin-first inspection tool for Kotlin / Android multi-module projects that turns dependency drift, Gradle inconsistency, architecture leaks, and coroutine anti-patterns into actionable terminal, JSON, HTML, Markdown, and SARIF reports.
Large Kotlin and Android codebases usually fail in the same places:
- module dependencies start drifting without a clear owner
- circular dependencies appear after a few rushed integrations
- Gradle configuration stops being consistent across modules
- architecture boundaries become documentation-only
- coroutine mistakes stay invisible until runtime symptoms appear
KInspector focuses on those engineering-governance problems instead of trying to be a generic lint wrapper.
- Scans Gradle multi-module structure from
settings.gradle(.kts)and module build files - Parses Android module metadata such as
namespace,compileSdk,minSdk,targetSdk, and Compose enablement - Resolves version catalog plugin aliases, library aliases, and bundles from
libs.versions.toml - Detects
includeBuild(...)setups and precompiled script plugins from build-logic projects - Flags convention plugins separately from standard Kotlin/Android/Java plugins
- Builds a project dependency graph for module-level analysis
- Detects circular module dependencies
- Flags architecture boundary violations using default layer heuristics or an ownership + allowlist policy
- Generates owner coverage reports so unresolved modules are visible in one place
- Tracks owner drift separately from issue drift so baseline comparisons show ownership regressions and resolutions
- Checks Gradle drift such as missing build files, missing plugin declarations, and inconsistent JVM toolchains
- Detects a first set of coroutine anti-patterns such as
GlobalScopeandrunBlockingin production code - Supports baseline/diff mode for CI, including
NEW,UNCHANGED, andFIXEDissue tracking - Supports owner drift gates, either as the default actionable set or as status-specific gates such as only
BECAME_UNOWNED - Emits GitHub Actions step summaries, PR comment Markdown, workflow annotations, and SARIF for code scanning
- Generates terminal, JSON, standalone HTML, Markdown, and SARIF reports
- Supports both CLI execution and Gradle task integration
- Designed around small interfaces so new rules and new report formats stay isolated
KInspector
├── core # Shared models and contracts
├── scanner # Project structure scanning
├── rules # Rule engine and default rules
├── reporter-json # JSON renderer
├── reporter-html # HTML renderer
├── reporter-markdown # GitHub PR summary Markdown renderer
├── reporter-sarif # SARIF renderer for code scanning
├── reporter-github # GitHub workflow annotations
├── cli # Command-line entrypoint
└── gradle-plugin # Gradle task integration
More design details:
./gradlew :cli:run --args="--project ."This prints a terminal report and also writes:
build/reports/kinspector/kinspector-report.jsonbuild/reports/kinspector/kinspector-report.htmlbuild/reports/kinspector/kinspector-report.mdwhen requestedbuild/reports/kinspector/kinspector-report.sarifwhen requested
To capture a baseline for CI:
./gradlew :cli:run --args="--project . --formats json --write-baseline kinspector-baseline.json"To compare against a baseline and fail only on new errors:
./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --fail-on-new-error"To emit only the baseline delta for review:
./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --diff-only --formats terminal,html,markdown,sarif"To emit a PR comment body and GitHub workflow annotations in CI:
./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --diff-only --formats markdown,sarif --pr-comment-file build/reports/kinspector/kinspector-pr-comment.md --pr-comment-mode compact --pr-comment-max-items 10 --pr-comment-overflow collapse --pr-comment-sort severity --write-github-step-summary --emit-github-annotations --sarif-category architecture-health"To switch terminal, Markdown, HTML, and SARIF message text to Chinese output:
./gradlew :cli:run --args="--project . --formats terminal,html,markdown,sarif --report-locale zh-cn"To emit SARIF under a stable GitHub code scanning category:
./gradlew :cli:run --args="--project . --formats sarif --sarif-category architecture-health"To write a GitHub PR summary directly into the current Actions job:
./gradlew :cli:run --args="--project . --formats markdown --write-github-step-summary"./gradlew :cli:installDist
./cli/build/install/kinspector/bin/kinspector --project .plugins {
id("io.github.kariowan.kinspector")
}
kInspector {
formats.set(listOf("terminal", "html", "json", "markdown", "sarif"))
failOnError.set(true)
failOnNewError.set(true)
failOnUnownedModule.set(true)
failOnOwnerDriftStatuses.set(listOf("BECAME_UNOWNED"))
baselineFile.set(layout.projectDirectory.file("kinspector-baseline.json"))
reportLocale.set("zh-cn")
sarifCategory.set("architecture-health")
prCommentFile.set(layout.buildDirectory.file("reports/kinspector/kinspector-pr-comment.md"))
prCommentMode.set("compact")
prCommentMaxItems.set(10)
prCommentOverflowStrategy.set("collapse")
prCommentIssueSortStrategy.set("severity")
writeGitHubStepSummary.set(true)
emitGitHubAnnotations.set(true)
diffOnly.set(true)
}./gradlew kInspectUse writeBaselineFile on the first run to create the baseline file. Add baselineFile when you want diff mode against an existing baseline. diffOnly requires baselineFile and filters reports down to NEW current issues plus FIXED issues in the human-readable diff sections. failOnUnownedModule requires ownership mode to be configured, otherwise KInspector fails fast because there is no owner coverage model to enforce. failOnOwnerDrift and failOnOwnerDriftStatuses require both a baseline and owner coverage data on the baseline/current runs.
KInspector supports both JSON and YAML config files. They are validated against the schema at core/src/main/resources/schema/kinspector-config.schema.json.
Use a config file to disable rules, change severity, or define architecture policy:
{
"rules": {
"gradle.missing-plugin": {
"enabled": false
},
"architecture.boundary": {
"severity": "ERROR"
}
},
"architecture": {
"moduleOwners": {
":app": "shell"
},
"packageOwners": {
"com.example.feature": "feature-team",
"com.example.core": "platform"
},
"ownerAllowlist": {
"shell": ["feature-team"],
"feature-team": ["platform"]
},
"dependencyRules": [
{
"name": "feature-boundary",
"from": {
"modules": [":feature:*"]
},
"allow": {
"layers": ["domain", "core"]
}
}
],
"moduleAllowlist": {
":app": [":feature:feed"]
}
}
}An example file is available at examples/kinspector.json.
YAML is also supported:
rules:
architecture.boundary:
severity: error
gradle.missing-plugin:
enabled: false
architecture:
moduleOwners:
:app: shell
packageOwners:
com.example.feature: feature-team
com.example.core: platform
ownerAllowlist:
shell:
- feature-team
feature-team:
- platform
dependencyRules:
- name: feature-boundary
from:
modules:
- :feature:*
allow:
layers:
- domain
- core
moduleAllowlist:
:app:
- :feature:feedKInspector :: SampleApp
Root :: /workspace/sample
Generated :: 2026-03-06T10:14:28+08:00
Summary :: modules=18, projectDeps=42, issues=5, errors=1, warnings=3, info=1
Modules
- :app [layer=app, toolchain=17] deps=[:feature:feed, :core:designsystem]
- :feature:feed [layer=feature, toolchain=17] deps=[:domain:feed, :core:common]
Issues
[ERROR] dependency.circular :: Circular dependency detected
Detected module cycle: :feature:feed -> :data:feed -> :feature:feed.
suggestion: Break the cycle by extracting shared contracts into a lower-level module.
{
"toolName": "KInspector",
"projectName": "SampleApp",
"summary": {
"moduleCount": 18,
"issueCount": 5,
"errorCount": 1,
"warningCount": 3,
"infoCount": 1
},
"issues": [
{
"ruleId": "dependency.circular",
"title": "Circular dependency detected",
"severity": "ERROR"
}
]
}The HTML report is standalone and CI-friendly. It includes:
- summary cards for module and issue counts
- baseline diff summary when a baseline is provided
- a sortable-at-a-glance issues table
- module dependency metadata for quick review
- owner coverage sections with grouped owners and unresolved modules
- version catalog and included build sections for build-logic visibility
The Markdown renderer is intended for PR checks and step summaries. It focuses on:
- top-level module and issue counts
- owner coverage and unresolved modules
- owner drift versus the baseline, including owner changes and newly unowned modules
- NEW issues when baseline diff mode is active
- FIXED issues when baseline diff mode is active
Use --pr-comment-file <path> or kInspector.prCommentFile when you want that Markdown body to be upserted into a PR comment by a workflow step.
Use --pr-comment-mode compact or kInspector.prCommentMode = "compact" when you want the comment to keep only:
- summary metrics
- owner drift summary
- NEW issues
- FIXED issues
Use --pr-comment-max-items <n> and --pr-comment-overflow collapse|truncate to control how many rows each compact section keeps before either:
- collapsing the remainder into a
<details>block - truncating the remainder with an omission note
Use --pr-comment-sort severity or reporting.prComment.issueSortStrategy: severity when the compact comment should show higher severity issues first before lower priority rows are pushed into the overflow section.
If you want to gate only specific ownership regressions, use --fail-on-owner-drift-statuses or kInspector.failOnOwnerDriftStatuses:
./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --fail-on-owner-drift-statuses BECAME_UNOWNED"kInspector {
failOnOwnerDriftStatuses.set(listOf("BECAME_UNOWNED"))
}The same behavior can live in kinspector.yaml / kinspector.json:
gates:
failOnOwnerDriftStatuses:
- became_unowned
reporting:
locale: zh-cn
prComment:
mode: compact
maxItems: 10
overflowStrategy: collapse
issueSortStrategy: severityKInspector is prepared for two release channels:
- JitPack for the publishable JVM modules such as
core,scanner, andrules - Gradle Plugin Portal for the Gradle plugin id
io.github.kariowan.kinspector
After pushing a release tag such as v0.1.0, JitPack consumers can use:
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.github.kariowan.kinspector:core:v0.1.0")
implementation("com.github.kariowan.kinspector:rules:v0.1.0")
}The Gradle plugin is configured for publication with:
- plugin id:
io.github.kariowan.kinspector - website:
https://github.com/kariowan/kinspector - VCS URL:
https://github.com/kariowan/kinspector.git
Until the plugin is published to the Gradle Plugin Portal, local and composite-build usage remain the reliable integration path inside this repository.
Use the SARIF renderer when you want GitHub code scanning ingestion:
./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --diff-only --formats markdown,sarif --write-github-step-summary --sarif-category architecture-health"The renderer emits:
ruleIdand severity level per issue- repo-relative artifact paths
partialFingerprints.primaryLocationLineHashfor stable alert matching- baseline state markers for current findings
- optional SARIF category via
--sarif-category <name>
Use --emit-github-annotations or kInspector.emitGitHubAnnotations to print workflow commands for:
- current issues in the visible report
- current unowned modules when ownership mode is active
- owner drift entries such as
OWNER_CHANGED,BECAME_UNOWNED,NEWLY_UNOWNED, andSTILL_UNOWNED
Example GitHub Actions step:
- name: Run KInspector
run: ./gradlew :cli:run --args="--project . --baseline kinspector-baseline.json --diff-only --formats markdown,sarif --pr-comment-file build/reports/kinspector/kinspector-pr-comment.md --pr-comment-mode compact --pr-comment-max-items 10 --pr-comment-overflow collapse --pr-comment-sort severity --write-github-step-summary --emit-github-annotations --sarif-category architecture-health --fail-on-owner-drift-statuses BECAME_UNOWNED"
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: build/reports/kinspector/kinspector-report.sarifA complete workflow template is available at examples/github-actions/kinspector-code-scanning.yml.
KInspector MVP is intentionally narrow:
- Kotlin / Android multi-module project scanning
- module dependency analysis
- Gradle consistency checks
- architecture boundary checks
- HTML and JSON reports
What it does not do in MVP:
- automatic fixes
- deep AST-based Kotlin semantic analysis
- full detekt integration
- interactive UI
The detailed MVP breakdown lives in docs/mvp.md.
- Add richer Gradle scanning for Android variants and plugin management edge cases
- Add ownership-aware package and module visualizations in reports
- Add SARIF quality gates and GitHub Action templates
- Add detekt bridge support for deeper Kotlin and coroutine analysis
- Add configuration file generated docs and migration guides
- Publish tagged releases to JitPack, the Gradle Plugin Portal, and standalone binaries
Contributions should keep the same constraints as the codebase:
- keep scanning, rules, and rendering decoupled
- prefer small interfaces and data classes over framework-heavy abstractions
- add new checks as isolated rules instead of embedding logic into reporters or entrypoints
- favor high-signal rules with low false-positive rates
Start here:
KInspector is available under the MIT License.