Aesthetic, minimalist platform for learning Japanese inspired by Duolingo and Monkeytype, built with Next.js and sponsored by Vercel. Beginner-friendly with plenty of good first issues - all contributions are welcome!
This report summarizes scanner evidence for lingdojo/kana-dojo, a TypeScript project with 3,769 GitHub stars. SynthScan examined 350,089 lines of code across 1197 source files, recording 206 pattern matches distributed across 11 syntactic categories. The legacy adjusted pattern signal is 0.8 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 180 finding(s).
Are imports declared and package/API names verified? 15 finding(s).
Are wrappers, guards, and repeated structures necessary? 116 finding(s).
Which changes need focused human review? 206 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 206 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | app/api/progress-sync/route.ts | 111 | function createRateLimitErrorResponse( | CODE |
| LOW | app/api/progress-sync/route.ts | 148 | async function upsertProgressSyncRecordAtomically( | CODE |
| LOW | app/api/progress-sync/lib.ts | 121 | export function buildProgressSyncStorageKey(syncKey: string): string { | CODE |
| LOW | app/api/progress-sync/lib.ts | 126 | export function validateProgressSyncRequestBody( | CODE |
| LOW | app/api/progress-sync/lib.ts | 212 | export function shouldAcceptIncomingUpdate( | CODE |
| LOW | features/Preferences/lib/auditThemes.ts | 78 | export function getThemesNeedingImprovement(): { | CODE |
| LOW | features/Preferences/lib/colorUtils.ts | 198 | export function getRelativeLuminanceFromRgb(rgb: RGB): number { | CODE |
| LOW | features/Preferences/data/wallpapers/wallpapers.ts | 38 | export function unregisterCustomWallpaper(id: string): void { | CODE |
| LOW | features/Preferences/data/audio/clickSounds.ts | 561 | export function getClickSoundVariantBaseUrls(id: ClickSoundId): string[] { | CODE |
| LOW | features/Preferences/data/themes/themeColors.ts | 182 | export function generateButtonBorderColor( | CODE |
| LOW | features/Preferences/data/themes/themes.ts | 151 | export function getThemeDefaultWallpaperId( | CODE |
| LOW | features/Preferences/data/themes/themes.ts | 280 | function buildCustomWallpaperTheme(id: string): Theme { | CODE |
| LOW | …es/Progress/components/stats/AchievementSummaryBar.tsx | 166 | export function getAchievementDisplayValues(summary: AchievementSummary): { | CODE |
| LOW | …ures/Progress/components/stats/TimedModeStatsPanel.tsx | 253 | export function getTimedModeDisplayValues(stats: TimedModeStats): { | CODE |
| LOW | features/Progress/components/stats/StatsPage.tsx | 286 | export function getStatsOverviewDisplayValues(stats: { | CODE |
| LOW | …Progress/components/stats/MasteryDistributionChart.tsx | 246 | export function getMasteryDistributionDisplayValues( | CODE |
| LOW | features/Progress/hooks/useStatsAggregator.ts | 52 | function createDefaultMasteryDistribution(): MasteryDistribution { | CODE |
| LOW | features/Progress/hooks/useStatsAggregator.ts | 90 | export function calculateMasteryDistribution( | CODE |
| LOW | features/Progress/hooks/useStatsAggregator.ts | 120 | function transformCharacterMastery( | CODE |
| LOW | features/Progress/hooks/useStatsAggregator.ts | 393 | export function filterCharacterMasteryByType( | CODE |
| LOW | features/Progress/hooks/useStatsAggregator.ts | 406 | export function getTopDifficultCharacters( | CODE |
| LOW | features/Progress/lib/setProgress.ts | 36 | export function calculateKanaSetProgressAndStars( | CODE |
| LOW⚡ | features/Progress/lib/setProgress.ts | 64 | export function getCappedVocabularyMeaningProgress(correct: number): number { | CODE |
| LOW⚡ | features/Progress/lib/setProgress.ts | 68 | export function getCappedVocabularyReadingProgress(correct: number): number { | CODE |
| LOW⚡ | features/Progress/lib/setProgress.ts | 72 | export function calculateKanjiSetProgress( | CODE |
| LOW | features/Progress/lib/setProgress.ts | 85 | export function calculateVocabularySetProgress( | CODE |
| LOW | features/Progress/lib/setProgress.ts | 101 | export function calculateKanjiSetProgressAndStars( | CODE |
| LOW | features/Progress/lib/setProgress.ts | 126 | export function calculateVocabularySetProgressAndStars( | CODE |
| LOW | features/Achievements/__tests__/meta.property.test.ts | 46 | function checkMetaAchievementRequirement( | CODE |
| LOW | …atures/Achievements/__tests__/mastery.property.test.ts | 40 | function createStatsForMasteryAchievement( | CODE |
| LOW | …ures/Achievements/__tests__/timeBased.property.test.ts | 23 | function createStatsForSpeedAchievement( | CODE |
| LOW | features/Achievements/__tests__/streak.property.test.ts | 18 | function createStatsForStreakAchievement( | CODE |
| LOW | …Achievements/__tests__/modeCompletion.property.test.ts | 65 | function createStatsForModeCompletion( | CODE |
| LOW | …Achievements/__tests__/modeCompletion.property.test.ts | 143 | function checkModeCompletionRequirement( | CODE |
| LOW | …ures/Achievements/__tests__/threshold.property.test.ts | 33 | function createStatsForThresholdAchievement( | CODE |
| LOW | …ures/Achievements/__tests__/threshold.property.test.ts | 129 | function checkThresholdRequirement( | CODE |
| LOW | …atures/Achievements/__tests__/variety.property.test.ts | 19 | function createStatsForVarietyAchievement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1764 | function checkDaysTrainedRequirement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1778 | function checkTimeOfDayRequirement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1799 | function checkWrongStreakRequirement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1813 | function checkExactCountRequirement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1830 | function checkAchievementCountRequirement( | CODE |
| LOW | features/Achievements/store/useAchievementStore.ts | 1855 | function checkTotalPointsRequirement( | CODE |
| LOW | features/Resources/__tests__/seo.property.test.ts | 54 | function generateCategoryPageMetadata( | CODE |
| LOW | features/Resources/__tests__/seo.property.test.ts | 96 | function generateSubcategoryPageMetadata( | CODE |
| LOW | features/Resources/lib/counts.ts | 21 | export function getCategoryResourceCounts( | CODE |
| LOW | features/Resources/lib/counts.ts | 40 | export function getSubcategoryResourceCounts( | CODE |
| LOW | features/Resources/lib/counts.ts | 64 | export function getResourceCountForCategory( | CODE |
| LOW | features/Resources/lib/counts.ts | 78 | export function getResourceCountForSubcategory( | CODE |
| LOW | features/Resources/lib/counts.ts | 94 | export function enrichCategoriesWithCounts( | CODE |
| LOW | features/Resources/lib/filters.ts | 48 | export function filterByCategoryAndSubcategory( | CODE |
| LOW | features/Resources/data/index.ts | 122 | export function getResourcesBySubcategory( | CODE |
| LOW | features/Resources/data/index.ts | 147 | export function getFeaturedResourcesByCategory( | CODE |
| LOW | features/Resources/data/index.ts | 176 | export function getSubcategoryResourceCount( | CODE |
| LOW | …ures/Translator/components/TranslationAlternatives.tsx | 29 | function generateLiteralTranslation(tokens: AnalyzedToken[]): string { | CODE |
| LOW | features/Conjugator/lib/seo/structuredData.ts | 138 | export function generateWebApplicationSchema(): Record<string, unknown> { | CODE |
| LOW | features/Conjugator/lib/seo/structuredData.ts | 362 | function generateOrganizationSchema(): Record<string, unknown> { | CODE |
| LOW | features/Conjugator/lib/engine/conjugateIchidan.ts | 251 | export function getIchidanColloquialPotential(verb: VerbInfo): string { | CODE |
| LOW | features/Conjugator/lib/engine/conjugateIchidan.ts | 268 | export function getIchidanTraditionalPotential(verb: VerbInfo): string { | CODE |
| LOW | features/AnkiConverter/parsers/colpkgParser.ts | 56 | export async function extractDatabaseFromCOLPKG( | CODE |
| 45 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | TODO_INDEXNOW_SETUP.md | 5 | ## Step 1: Generate UUID Key | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 17 | ## Step 2: Create Public Key File | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 36 | ## Step 3: Add Environment Variable | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 48 | ## Step 4: Add to Production Environment | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 62 | ## Step 5: Commit and Deploy | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 77 | ## Step 6: Verify After Deployment | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 100 | ## Step 7: Submit to Bing Webmaster Tools | COMMENT |
| LOW | TODO_INDEXNOW_SETUP.md | 111 | ## Step 8: Integrate into Your Workflow | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 284 | // Step 1: Remove media tags | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 287 | // Step 2: Convert formatting tags to text markers (if enabled) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 292 | // Step 3: Decode HTML entities | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 295 | // Step 4: Strip remaining HTML tags | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 298 | // Step 5: Clean whitespace (if enabled) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 108 | // Step 1: Remove control characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 111 | // Step 2: Replace Anki deck separator (::) with a safe alternative BEFORE replacing individual colons | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 114 | // Step 3: Replace invalid filename characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 117 | // Step 4: Collapse multiple replacement characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 125 | // Step 5: Trim whitespace and replacement characters from ends (repeat until stable) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 133 | // Step 6: Handle Windows reserved names | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 148 | // Step 7: Handle empty result after sanitization | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 153 | // Step 8: Enforce length limit (accounting for extension) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 161 | // Step 9: Add extension if requested | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 15 | ### Step 1: Create Metadata File | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 25 | ### Step 2: Translate Metadata | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 50 | ### Step 3: Update Routing Configuration | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 61 | ### Step 4: Update Root Layout Metadata | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 74 | ### Step 5: Update Sitemap Configuration | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 80 | ### Step 6: Test Your New Language | COMMENT |
| LOW | docs/ADDING_LANGUAGES.md | 93 | ### Step 7: Build and Verify | COMMENT |
| LOW | docs/SEO.md | 13 | ### Step 1: Import Metadata Type | COMMENT |
| LOW | docs/SEO.md | 19 | ### Step 2: Export Metadata Object | COMMENT |
| LOW | docs/SEO.md | 43 | ### Step 3: Test Your Changes | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 284 | // Step 1: Remove media tags | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 287 | // Step 2: Convert formatting tags to text markers (if enabled) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 292 | // Step 3: Decode HTML entities | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 295 | // Step 4: Strip remaining HTML tags | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/textExtractor.ts | 298 | // Step 5: Clean whitespace (if enabled) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 108 | // Step 1: Remove control characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 111 | // Step 2: Replace Anki deck separator (::) with a safe alternative BEFORE replacing individual colons | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 114 | // Step 3: Replace invalid filename characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 117 | // Step 4: Collapse multiple replacement characters | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 125 | // Step 5: Trim whitespace and replacement characters from ends (repeat until stable) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 133 | // Step 6: Handle Windows reserved names | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 148 | // Step 7: Handle empty result after sanitization | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 153 | // Step 8: Enforce length limit (accounting for extension) | COMMENT |
| LOW⚡ | features/AnkiConverter/lib/filenameSanitizer.ts | 161 | // Step 9: Add extension if requested | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | docker-compose.yml | 21 | # Production environment (uncomment for production) | COMMENT |
| LOW | features/Demo/components/DemoGame.tsx | 41 | // TEMPORARILY DISABLED - focusing on kanji only | COMMENT |
| LOW | features/Demo/components/DemoGame.tsx | 361 | COMMENT | |
| LOW | features/Preferences/fonts.ts | 1 | // Heavy entrypoint: explicit import surface for font catalog only. | COMMENT |
| LOW | features/Preferences/index.ts | 21 | } from './facade'; | COMMENT |
| LOW | features/Resources/index.ts | 101 | // Hooks | COMMENT |
| LOW | features/Kana/index.ts | 21 | export type { KanaCharacter, KanaGroup } from '@/entities/kana'; | COMMENT |
| LOW | features/Kanji/index.ts | 21 | // PRIVATE - DO NOT IMPORT DIRECTLY | COMMENT |
| LOW | features/Conjugator/lib/engine/conjugateIchidan.ts | 101 | // ============================================================================ | COMMENT |
| LOW | features/Conjugator/lib/engine/conjugateIchidan.ts | 161 | COMMENT | |
| LOW | features/Vocabulary/index.ts | 21 | // PRIVATE - DO NOT IMPORT DIRECTLY | COMMENT |
| LOW | docs/archived/ARCHITECTURE_DIAGRAM.md | 421 | // ❌ BLOCKED: shared/ → features/ | COMMENT |
| LOW | supabase/config.toml | 1 | # For detailed configuration reference documentation, visit: | COMMENT |
| LOW | supabase/config.toml | 21 | # without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud | COMMENT |
| LOW | supabase/config.toml | 121 | # [storage.buckets.images] | COMMENT |
| LOW | supabase/config.toml | 161 | # external_url = "" | COMMENT |
| LOW | supabase/config.toml | 181 | # Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. | COMMENT |
| LOW | supabase/config.toml | 201 | sms_sent = 30 | COMMENT |
| LOW | supabase/config.toml | 221 | enable_signup = true | COMMENT |
| LOW | supabase/config.toml | 241 | # user = "apikey" | COMMENT |
| LOW | supabase/config.toml | 261 | enable_confirmations = false | COMMENT |
| LOW | supabase/config.toml | 401 | s3_region = "env(S3_REGION)" | COMMENT |
| LOW | .github/dependabot.yml | 61 | # Always create security PRs immediately | COMMENT |
| LOW | openspec/config.yaml | 1 | schema: spec-driven | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | …eferences/components/renderers/CursorTrailRenderer.tsx | 8 | // ─── Particle (flat struct, no strings at draw time) ────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/CursorTrailRenderer.tsx | 23 | // ─── Component ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/CursorTrailRenderer.tsx | 64 | // ── Throttled mousemove: spawn max 1 particle every 30ms ────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/CursorTrailRenderer.tsx | 107 | // ── Render loop ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/ClickEffectRenderer.tsx | 7 | // ─── Particle ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/ClickEffectRenderer.tsx | 22 | // ─── Component ──────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/ClickEffectRenderer.tsx | 61 | // ── Spawn burst ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | …eferences/components/renderers/ClickEffectRenderer.tsx | 105 | // ── Render loop (only runs while particles exist) ───────────────────────── | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | docs/session-history-implementation.md | 113 | #### UX behavior now (as requested) | COMMENT |
| HIGH | docs/github-gfi-visibility-research.md | 631 | ## 11) Risk-tiered execution policy (as requested) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | core/i18n/locales/zh/translator.json | 5 | "placeholder": "输入要翻译的文本...", | CODE |
| LOW | core/i18n/locales/zh/conjugator.json | 15 | "placeholder": "输入日语动词...", | CODE |
| LOW | core/i18n/locales/fr/translator.json | 16 | "placeholder": { | CODE |
| LOW | core/i18n/locales/fr/translator.json | 26 | "placeholder": { | CODE |
| LOW | core/i18n/locales/fr/resources.json | 8 | "placeholder": "Rechercher des ressources...", | CODE |
| LOW | core/i18n/locales/fr/conjugator.json | 15 | "placeholder": "日本語の動詞を入力...", | CODE |
| LOW | core/i18n/locales/es/translator.json | 16 | "placeholder": { | CODE |
| LOW | core/i18n/locales/es/translator.json | 26 | "placeholder": { | CODE |
| LOW | core/i18n/locales/es/resources.json | 8 | "placeholder": "Buscar recursos...", | CODE |
| LOW | core/i18n/locales/es/conjugator.json | 15 | "placeholder": "日本語の動詞を入力...", | CODE |
| LOW | core/i18n/locales/en/translator.json | 16 | "placeholder": { | CODE |
| LOW | core/i18n/locales/en/translator.json | 26 | "placeholder": { | CODE |
| LOW | core/i18n/locales/en/resources.json | 8 | "placeholder": "Search resources...", | CODE |
| LOW | core/i18n/locales/en/conjugator.json | 15 | "placeholder": "日本語の動詞を入力...", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH⚡ | batch_new.py | 35 | run("git checkout main --quiet && git pull -q nn main") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | features/Experiments/components/RunnerGame.tsx | 410 | // Draw twice for seamless scrolling | COMMENT |
| MEDIUM | …ures/Translator/components/TranslationAlternatives.tsx | 135 | // Common Japanese phrases with nuanced English translations | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | batch_new.py | 24 | except Exception: | CODE |
| LOW⚡ | batch_new.py | 27 | except Exception: entry = raw | CODE |
| LOW⚡ | batch_new.py | 44 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | docs/ARCHITECTURE.md | 185 | └── TrainingGame/ # Shared training orchestration widget | CODE |