summaryrefslogtreecommitdiff
path: root/config/detekt/baseline.xml
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-24 07:54:01 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-24 07:54:01 +0000
commitda942624a4abd82797f563cf8c57f1124c5de435 (patch)
treef7e4325a4783a77544e6ed1f0ac44ea844757fe8 /config/detekt/baseline.xml
parentb299d57d6cc240381aed51f91e5a26f1e633d78d (diff)
Add TDD quality gate: detekt, coverage tracking, pre-commit hook; fix theme crash risk
CLAUDE.md now documents mandatory red-green-refactor TDD, a per-commit quality gate (build, tests, lint, detekt, coverage-non-decrease, WebView-JS justification, manifest permission diff, secret scan), and why each check exists (tracing back to real bugs this audit found). Wires up detekt (static analysis) and a standalone Jacoco setup (scoped to data/readability/opml/work/stash -- ui/ Compose screens are impractical to unit-test for coverage) via app/build.gradle.kts. Bumped Kotlin/KSP to 2.0.21/2.0.21-1.0.28 to match detekt 1.23.8's compatibility requirement. Also fixes the themes.xml bug the lint gate immediately caught: Theme.FeedReader's parent required API 29 while minSdk is 26 -- a real crash risk on Android 8.0-9.0 devices. Swapped to the plain (non-DayNight) DeviceDefault parent, since Compose already owns all real theming via a hardcoded darkColorScheme() regardless of system day/night state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
Diffstat (limited to 'config/detekt/baseline.xml')
-rw-r--r--config/detekt/baseline.xml77
1 files changed, 77 insertions, 0 deletions
diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
new file mode 100644
index 0000000..bd02fbe
--- /dev/null
+++ b/config/detekt/baseline.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" ?>
+<SmellBaseline>
+ <ManuallySuppressedIssues/>
+ <CurrentIssues>
+ <ID>ComplexCondition:FeedParser.kt$FeedParser$inEntry &amp;&amp; rel == "enclosure" &amp;&amp; type?.startsWith("image/") == true &amp;&amp; href != null</ID>
+ <ID>CyclomaticComplexMethod:ArticleListScreen.kt$@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, ExperimentalCoroutinesApi::class) @Composable fun ArticleListScreen( feedId: String, onOpenArticle: (String) -&gt; Unit, onBack: () -&gt; Unit, onOpenFeedList: () -&gt; Unit )</ID>
+ <ID>CyclomaticComplexMethod:FeedFetchWorker.kt$FeedFetchWorker$private suspend fun fetchOne(feed: FeedEntity, db: AppDatabase): Int</ID>
+ <ID>CyclomaticComplexMethod:FeedParser.kt$FeedParser$private fun parseAtom(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>CyclomaticComplexMethod:FeedParser.kt$FeedParser$private fun parseRss(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>CyclomaticComplexMethod:LinkReaderScreen.kt$@SuppressLint("SetJavaScriptEnabled") @OptIn(ExperimentalMaterial3Api::class) @Composable fun LinkReaderScreen(url: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit)</ID>
+ <ID>CyclomaticComplexMethod:ReaderScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ReaderContent( articleId: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit, hasPrevious: Boolean, hasNext: Boolean, onPrevious: () -&gt; Unit, onNext: () -&gt; Unit, onNextKeepUnread: () -&gt; Unit )</ID>
+ <ID>LongMethod:AddFeedScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun AddFeedScreen(onDone: () -&gt; Unit)</ID>
+ <ID>LongMethod:ArticleListScreen.kt$@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, ExperimentalCoroutinesApi::class) @Composable fun ArticleListScreen( feedId: String, onOpenArticle: (String) -&gt; Unit, onBack: () -&gt; Unit, onOpenFeedList: () -&gt; Unit )</ID>
+ <ID>LongMethod:FeedFetchWorker.kt$FeedFetchWorker$private suspend fun fetchOne(feed: FeedEntity, db: AppDatabase): Int</ID>
+ <ID>LongMethod:FeedListScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun FeedListScreen( onOpenFeed: (String) -&gt; Unit, onOpenUnread: () -&gt; Unit, onOpenOlder: () -&gt; Unit, onOpenSaved: () -&gt; Unit, onAddFeed: () -&gt; Unit, onSettings: () -&gt; Unit )</ID>
+ <ID>LongMethod:FeedParser.kt$FeedParser$private fun parseAtom(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>LongMethod:FeedParser.kt$FeedParser$private fun parseRss(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>LongMethod:LinkReaderScreen.kt$@SuppressLint("SetJavaScriptEnabled") @OptIn(ExperimentalMaterial3Api::class) @Composable fun LinkReaderScreen(url: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit)</ID>
+ <ID>LongMethod:MainActivity.kt$@Composable fun FeedReaderApp()</ID>
+ <ID>LongMethod:ReaderExtractor.kt$ReaderExtractor.&lt;no name provided&gt;$override fun onPageFinished(view: WebView, finishedUrl: String)</ID>
+ <ID>LongMethod:ReaderScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ReaderContent( articleId: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit, hasPrevious: Boolean, hasNext: Boolean, onPrevious: () -&gt; Unit, onNext: () -&gt; Unit, onNextKeepUnread: () -&gt; Unit )</ID>
+ <ID>LongMethod:SettingsScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen(onBack: () -&gt; Unit)</ID>
+ <ID>LongParameterList:ArticleListScreen.kt$( article: ArticleListItem, highlighted: Boolean, showFeedName: Boolean, showReadState: Boolean, onClick: () -&gt; Unit, onMarkReadFrom: () -&gt; Unit )</ID>
+ <ID>LongParameterList:Daos.kt$FeedDao$( id: String, title: String, siteUrl: String?, lastFetchedAt: Long, etag: String?, lastModified: String?, lastError: String? )</ID>
+ <ID>LongParameterList:FeedListScreen.kt$( onOpenFeed: (String) -&gt; Unit, onOpenUnread: () -&gt; Unit, onOpenOlder: () -&gt; Unit, onOpenSaved: () -&gt; Unit, onAddFeed: () -&gt; Unit, onSettings: () -&gt; Unit )</ID>
+ <ID>LongParameterList:ReaderScreen.kt$( articleId: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit, hasPrevious: Boolean, hasNext: Boolean, onPrevious: () -&gt; Unit, onNext: () -&gt; Unit, onNextKeepUnread: () -&gt; Unit )</ID>
+ <ID>LongParameterList:ReaderScreen.kt$( feedId: String, articleId: String, onBack: () -&gt; Unit, onOpenLink: (String) -&gt; Unit, onOpenBrowser: (String) -&gt; Unit, onNavigateToArticle: (String) -&gt; Unit )</ID>
+ <ID>LongParameterList:ReaderWebView.kt$( context: Context, onOpenLink: (String) -&gt; Unit, onLongPressLink: (String) -&gt; Unit = {}, onYouTubeVideo: (String) -&gt; Unit = {}, onScrollChanged: (Int) -&gt; Unit = {}, onPageFinished: (WebView) -&gt; Unit = {} )</ID>
+ <ID>LongParameterList:ReaderWebView.kt$( url: String, onDismiss: () -&gt; Unit, onOpenInBrowser: () -&gt; Unit, onShare: () -&gt; Unit, onSaveToStash: () -&gt; Unit, onSaveForLater: () -&gt; Unit )</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$10</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$11</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$3</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$4</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$5</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$6</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$7</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$8</ID>
+ <ID>MagicNumber:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$9</ID>
+ <ID>MagicNumber:Daos.kt$500</ID>
+ <ID>MagicNumber:FeedFetchWorker.kt$FeedFetchWorker$304</ID>
+ <ID>MagicNumber:ReaderWebView.kt$16f</ID>
+ <ID>MagicNumber:ReaderWebView.kt$9f</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$200</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$201</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$35</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$401</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$403</ID>
+ <ID>MagicNumber:StashClient.kt$StashClient$409</ID>
+ <ID>MatchingDeclarationName:ReaderWebView.kt$ReaderScrollPositions</ID>
+ <ID>MaxLineLength:Daos.kt$ArticleDao$"WHERE (articles.isRead = 0 OR articles.readAt &gt;= :sinceFetch) AND (articles.publishedAt IS NULL OR articles.publishedAt &gt;= :cutoff) "</ID>
+ <ID>MaxLineLength:Daos.kt$ArticleDao$"WHERE (articles.isRead = 0 OR articles.readAt &gt;= :sinceFetch) AND articles.publishedAt IS NOT NULL AND articles.publishedAt &lt; :cutoff "</ID>
+ <ID>MaxLineLength:Entities.kt$ArticleEntity$@ColumnInfo(index = true) override val feedId: String?</ID>
+ <ID>MaxLineLength:Entities.kt$ArticleEntity$val imageUrl: String? = null</ID>
+ <ID>MaxLineLength:Entities.kt$ArticleEntity$val readAt: Long? = null</ID>
+ <ID>MaxLineLength:LinkReaderScreen.kt$loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null)</ID>
+ <ID>MaxLineLength:LinkReaderScreen.kt$webView.loadDataWithBaseURL(url, wrapReaderHtml(currentExtractedContent, extractedTitle), "text/html", "UTF-8", null)</ID>
+ <ID>MaxLineLength:ReaderScreen.kt$loadDataWithBaseURL(baseUrl, wrapReaderHtml(content, article?.title, article?.publishedAt, article?.imageUrl), "text/html", "UTF-8", null)</ID>
+ <ID>MaxLineLength:ReaderScreen.kt$webView.loadDataWithBaseURL(baseUrl, wrapReaderHtml(content, article?.title, article?.publishedAt, article?.imageUrl), "text/html", "UTF-8", null)</ID>
+ <ID>MaxLineLength:ReaderWebView.kt$"&lt;figure&gt;&lt;a href=\"https://www.youtube.com/watch?v=$id\"&gt;&lt;img src=\"https://i.ytimg.com/vi/$id/hq720.jpg\" alt=\"YouTube video\"&gt;&lt;/a&gt;&lt;figcaption&gt;▶ Watch on YouTube&lt;/figcaption&gt;&lt;/figure&gt;"</ID>
+ <ID>MaxLineLength:SettingsScreen.kt$DropdownMenuItem(text = { Text("Highlight") }, onClick = { newRuleAction = FilterAction.HIGHLIGHT; actionMenuExpanded = false })</ID>
+ <ID>MaxLineLength:SettingsScreen.kt$DropdownMenuItem(text = { Text("Mute") }, onClick = { newRuleAction = FilterAction.MUTE; actionMenuExpanded = false })</ID>
+ <ID>NestedBlockDepth:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$override fun migrate(db: SupportSQLiteDatabase)</ID>
+ <ID>NestedBlockDepth:FeedFetchWorker.kt$FeedFetchWorker$private suspend fun fetchOne(feed: FeedEntity, db: AppDatabase): Int</ID>
+ <ID>NestedBlockDepth:FeedParser.kt$FeedParser$private fun parseAtom(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>NestedBlockDepth:FeedParser.kt$FeedParser$private fun parseRss(parser: XmlPullParser): ParsedFeed</ID>
+ <ID>NestedBlockDepth:Opml.kt$Opml$fun parse(input: InputStream): List&lt;OpmlFeed&gt;</ID>
+ <ID>ReturnCount:ReaderWebView.kt$&lt;no name provided&gt;$override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean</ID>
+ <ID>TooGenericExceptionCaught:AddFeedScreen.kt$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:AppDatabase.kt$AppDatabase.Companion$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:AppDatabase.kt$AppDatabase.Companion.&lt;no name provided&gt;$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:FeedFetchWorker.kt$FeedFetchWorker$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:MainActivity.kt$MainActivity$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:MorningFetchReceiver.kt$MorningFetchReceiver$e: Exception</ID>
+ <ID>TooGenericExceptionCaught:MorningFetchReceiver.kt$MorningFetchReceiver.Companion$e: Exception</ID>
+ <ID>TooManyFunctions:Daos.kt$ArticleDao</ID>
+ </CurrentIssues>
+</SmellBaseline>