{"version": "2.1.0", "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "runs": [{"tool": {"driver": {"name": "Repobility", "informationUri": "https://repobility.com", "rules": [{"id": "scanner-27bcaecf13b7f79a", "name": "Possibly dead Python function: download_file", "shortDescription": {"text": "Possibly dead Python function: download_file"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e3ff781abcfa2f80", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/components/AppShell.tsx:142", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/components/AppShell.tsx:142"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-85bb8fc37d9fa962", "name": "TODO/FIXME marker in shipping code \u2014 packages/client/src/pages/GamesHubPage.tsx:34", "shortDescription": {"text": "TODO/FIXME marker in shipping code \u2014 packages/client/src/pages/GamesHubPage.tsx:34"}, "fullDescription": {"text": "Track in /reviews or /issues, not as a code comment that rots.\n\nWhy: Drift control \u2014 shouldn't be the same as Quality TODO scanner.\nRule id: fq.todo-marker"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7c563585b4a296ad", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/BookDetailPage.tsx:272", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/BookDetailPage.tsx:272"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-45b9a1fcc9b4f663", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/LibraryMasterPage.tsx:464", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/LibraryMasterPage.tsx:464"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-59a7bd3f1eec4bb8", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/design-system/primitives/PageHeader.tsx:34", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/design-system/primitives/PageHeader.tsx:34"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-a86a339797bd0c5b", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SettingsTab.tsx:44", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SettingsTab.tsx:448"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-0982e744ec5fc8b0", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/PhonicsAudioLibrar", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/PhonicsAudioLibrary.tsx:392"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-d2cde09e0a85d9d8", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SystemSoundsLibrar", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SystemSoundsLibrary.tsx:365"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-8305ad66b9e0bf33", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/KoreanPhoni", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/KoreanPhonicsStudyPage.tsx:154"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-810e66e0901f2bdf", "name": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/KoreanPhonicsStud", "shortDescription": {"text": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/KoreanPhonicsStudyPage.tsx:244"}, "fullDescription": {"text": "A ternary like `active ? 'bg-white' : '...'` (or bg-gray-100/200) on a dark theme produces jarring white pills. Use a dark-emphasized active state instead \u2014 border + ring or slightly brighter dark bg. Example: `active ? 'bg-gray-800 border-gray-500 ring-1 ring-blue-500/30' : '\u2026'`.\n\nWhy: P-E in CHECKLIST.md \u2014 light bg in a dark theme is a class of regression.\nRule id: fq.active-light-bg"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-aa209194bd100619", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/EnglishPhon", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/EnglishPhonicsStudyPage.tsx:119"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ee583d28f1b2b384", "name": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/EnglishPhonicsStu", "shortDescription": {"text": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/EnglishPhonicsStudyPage.tsx:198"}, "fullDescription": {"text": "A ternary like `active ? 'bg-white' : '...'` (or bg-gray-100/200) on a dark theme produces jarring white pills. Use a dark-emphasized active state instead \u2014 border + ring or slightly brighter dark bg. Example: `active ? 'bg-gray-800 border-gray-500 ring-1 ring-blue-500/30' : '\u2026'`.\n\nWhy: P-E in CHECKLIST.md \u2014 light bg in a dark theme is a class of regression.\nRule id: fq.active-light-bg"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1a727bc8ab4b281c", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogPostCard.tsx:139", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogPostCard.tsx:139"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-eb00d251d1271a9c", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:88", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:88"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9064123593082bb8", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:99"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-fdc3b19ab780f01f", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogSectionEditor.tsx:", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogSectionEditor.tsx:209"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-cc2257a5da98a3c8", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategorySection.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategorySection.tsx:26"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-d7bb3e54483067ec", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookMatrixModal.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookMatrixModal.tsx:260"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5094852e53390d7e", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCardEditable.ts", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCardEditable.tsx:122"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-a4b0b591d3c20347", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCard.tsx:48", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCard.tsx:48"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-b66dbe97f33d3888", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryPanel.tsx:1", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryPanel.tsx:184"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-6504cf76cee04537", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryChipDropdow", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryChipDropdown.tsx:38"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-21713489177efda0", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GamePreviewModal.tsx:43", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GamePreviewModal.tsx:43"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-62b51054eaaaabda", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GameCreatorModal.tsx:65", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GameCreatorModal.tsx:65"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9113abe2cef93040", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/DotEditorModal.tsx:48", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/DotEditorModal.tsx:48"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ba72bb3972e8d2cf", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/key-object/components/KeyObjectTab.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/key-object/components/KeyObjectTab.tsx:609"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-1219a5a7dcf071df", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/key-object/components/KeyObjectDotEditorModal.ts", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/key-object/components/KeyObjectDotEditorModal.tsx:39"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-13050eced504f538", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/tts/components/TtsTab.tsx:112", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/tts/components/TtsTab.tsx:112"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-9669a972802b1ff3", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/KoreanPhonicsHeatm", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/KoreanPhonicsHeatmap.tsx:191"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-b5771922c0991f30", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/StorybookReportSec", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/StorybookReportSection.tsx:63"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-bf5925ec6339d53f", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/ArtStyleDistributi", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/ArtStyleDistributionCard.tsx:83"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-b9b66f44a73deadd", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/VocabularyMasteryC", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/VocabularyMasteryCard.tsx:54"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-287eda19138f6e79", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/EnglishPhonicsSkil", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/EnglishPhonicsSkillTree.tsx:105"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ec2ac33b9e1fbb15", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/PhonicsViewer.tsx:11", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/PhonicsViewer.tsx:114"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3c7d0bb163737a25", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/ViewerToolbar.tsx:81", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/ViewerToolbar.tsx:81"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-aa62310562d4b7be", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/QuizViewer.tsx:71", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/QuizViewer.tsx:71"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f6c689cfc0c47be2", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:1", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:110"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-88da80d50f3f2824", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/CopyProgressModal", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/CopyProgressModal.tsx:80"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f70c443003eaf101", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/SidebarCard.tsx:1", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/SidebarCard.tsx:146"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-0ddc44b4c1c379e5", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/Sidebar.tsx:131", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/Sidebar.tsx:131"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7f1a8e5daac55990", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/StorybookCard.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/StorybookCard.tsx:69"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-137e335ebae0e8f4", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/ProjectSwi", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/ProjectSwitcher.tsx:38"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-8449cd1b3d715c5d", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/SidebarNav", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/SidebarNavItem.tsx:25"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c0e95c6064393092", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/PublishQu", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/PublishQueue.tsx:358"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-0f4e4724924327b7", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/BulkSched", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/BulkScheduleDialog.tsx:245"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-10760671df4e0d6d", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/TopPage", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/TopPagesTable.tsx:49"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7ee33ca7e5a581e9", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/Content", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/ContentPerformance.tsx:39"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-bedc2671a1f12a6b", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/seo/Seo", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:363"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-77c7bf848cfa84c4", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/analytics/seo/Se", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:437"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ccf3c2861fcc184c", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ads/AdsDashboard.", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ads/AdsDashboard.tsx:345"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-07f7b30d6969c06a", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/competitors/Compe", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/competitors/CompetitorsDashboard.tsx:450"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-4dff0d3f5a7f60b6", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/BlogPrev", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/BlogPreviewDialog.tsx:155"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-0f6c50eee7405ab3", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubeCa", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubeCardItem.tsx:104"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5fe4259f54487887", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/BaseArtic", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/BaseArticlePanel.tsx:218"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5b95fba94cb9638c", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/ChannelT", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/ChannelTranslationView.tsx:43"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-30cdcecb4206d6fd", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/CardNewsP", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/CardNewsPanel.tsx:177"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-03b7b1046c099e20", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubePa", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubePanel.tsx:434"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-e65cfe4ff025574f", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/ideas/GoldenTier", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx:117"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-0c92f75325a4947d", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ideas/TrendingFee", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ideas/TrendingFeed.tsx:68"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c7331e4f5384935c", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/BgmSection.tsx:177"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ae96ae190bb69048", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/WritingGuideSection.tsx:204"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5b9aada34a51592e", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:273"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-e3151580adfb9621", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/LongformProj", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/LongformProjectHeader.tsx:23"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-89a79761fd289f92", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/TimelineEdit", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/TimelineEditorStep.tsx:628"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-1d4659a9a90947c8", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/PromptPreset", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/PromptPresetModal.tsx:174"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ca60369d20e32df0", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyS", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyStudyPage.tsx:117"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7627f602dc0e1316", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyU", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyUnitSidebarList.tsx:130"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-2962dcd3fe50fd50", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyS", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyStudyContent.tsx:204"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-781481823e86c9de", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary/components/VocabTreeModal.t", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary/components/VocabTreeModal.tsx:265"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f2d02ae53756b2f9", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/LevelEditCard.tsx:16", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/LevelEditCard.tsx:167"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-cd2b9e59a37ba754", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/OtherStyleReference.", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/OtherStyleReference.tsx:93"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-a0ff377425574604", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/BookManageTab.tsx:62", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/BookManageTab.tsx:62"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-dcfe885a8c6387ad", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/EditorPanelV2.tsx:15", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/EditorPanelV2.tsx:157"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-554925ff707d6426", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterTab.tsx:", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterTab.tsx:343"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-78c9c35d13e151ec", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterLibraryM", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterLibraryModal.tsx:345"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3b62098860021068", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics/components/TtsRow.tsx:47", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics/components/TtsRow.tsx:47"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-732552c441693bae", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/phonics/components/HotspotEditorModal.tsx:317", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/phonics/components/HotspotEditorModal.tsx:317"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1b5e8c71241e52a9", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/audiobook/components/AudiobookProjectC", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/audiobook/components/AudiobookProjectCard.tsx:655"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-50968962711495d9", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/YouTubeUploadModal.tsx:179", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/YouTubeUploadModal.tsx:179"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-de31b1d5de9b57c0", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/GamesTab.tsx:174", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/GamesTab.tsx:174"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-105f47d40c84b422", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/AudiobookRenderModal.tsx:101", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/AudiobookRenderModal.tsx:101"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c150f364964c2410", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CaptionsModal.tsx:142", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CaptionsModal.tsx:142"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2a8a2b9ee92b18f4", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/MarketingTab.tsx:13", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/MarketingTab.tsx:134"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-9af4cc53925fee39", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/TextTab.tsx:225", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/TextTab.tsx:225"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ff0e1e2619d5800d", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx:253", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx:253"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-cf68c940362aaf70", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx:205"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c48c0f4b0d8aa5ad", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/LongformTab.tsx:328", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/LongformTab.tsx:328"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-9f920a6ca17f28b4", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CreateLongformModal.tsx:52", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CreateLongformModal.tsx:52"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-091af1e235ea215a", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/SceneEditor.tsx:65", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/SceneEditor.tsx:65"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7da8bb8912d6b036", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/AudiobookTab.tsx:23", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/AudiobookTab.tsx:232"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ba2d2e990b445d70", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsPreviewModal.tsx:15", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsPreviewModal.tsx:158"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3886d48b22593afb", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsConfigFor", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsConfigForm.tsx:126"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-80b82ae6a40592fe", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsProjectCa", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsProjectCard.tsx:97"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7031ab52b0c8b255", "name": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsSlideEditor.tsx:75", "shortDescription": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsSlideEditor.tsx:75"}, "fullDescription": {"text": "A `<button>` whose only child is a single glyph or symbol needs `title=` or `aria-label=` so screen readers (and tooltips on hover) work.\n\nWhy: P3 in CHECKLIST.md \u2014 icon-only buttons skipped a title.\nRule id: fq.button.no-label"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1b03c242cc83b8e4", "name": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsSlideEdit", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsSlideEditor.tsx:160"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-370f10af52ade80b", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/restore-from-manifest.ts:12", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/restore-from-manifest.ts:12"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-854f65852f63c5dd", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/check-storybook-urls.ts:28", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/check-storybook-urls.ts:28"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-abd73e6010ae6eb2", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/find-storybook.ts:11", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/find-storybook.ts:11"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1b3303b4df3197e0", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/migrate-assets.ts:33", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/migrate-assets.ts:33"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-32bc89c55ed2fa69", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/count-manifests.ts:6", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/count-manifests.ts:6"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b50bd503ad4a5f1c", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-url-walker.ts:7", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-url-walker.ts:7"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-cc3027b740f2149c", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-transcode.ts:15", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-transcode.ts:15"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d9260469ac09ed9c", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/utils/remotion-bundle.ts:39", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/utils/remotion-bundle.ts:39"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6436dee63eb03109", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/grok.provider.ts:58", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/grok.provider.ts:58"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-751694188133583a", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/youtube.provider.ts:120", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/youtube.provider.ts:120"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d1e41df8cc64df6a", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/longform.provider.ts:121", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/longform.provider.ts:121"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-77e4b053cd3f0a03", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/audiobook.service.ts:70", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/audiobook.service.ts:70"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-153c56276a7b3c40", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/storybook.service.ts:206", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/storybook.service.ts:206"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0a0b4a8a989d3a92", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/longform.service.ts:717", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/longform.service.ts:717"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b7c3b7670d83ee0d", "name": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/mkt/external/__tests__/naver-searchad.test.ts:13", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/mkt/external/__tests__/naver-searchad.test.ts:13"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d63da3583b14afc0", "name": "Dockerfile runs as root: Dockerfile", "shortDescription": {"text": "Dockerfile runs as root: Dockerfile"}, "fullDescription": {"text": "No non-root USER set. Containers running as root expand the blast radius of any vulnerability inside the image."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8987d6d1c30c7202", "name": "Docker base image is tag-pinned but not digest-pinned: node:20-alpine", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: node:20-alpine"}, "fullDescription": {"text": "Container tags can be retagged upstream. Pin production base images to a reviewed digest (`image@sha256:...`) when reproducibility and supply-chain integrity matter."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "low", "confidence": 1.0}}, {"id": "scanner-aa5acaa49eb8315b", "name": "Containers defined but no K8s/orchestration manifest found", "shortDescription": {"text": "Containers defined but no K8s/orchestration manifest found"}, "fullDescription": {"text": "Repo has Dockerfiles/compose but no Kubernetes/Nomad manifests. If the target deployment is K8s, the manifests may live in a separate ops repo."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0befc492f97c4f13", "name": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master.html:403", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master.html:403"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-21c82ef57db50efb", "name": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/curriculum-master.html:457", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/curriculum-master.html:457"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b460a7e75f82c5c9", "name": "Insecure pattern 'insert_adjacent_html' in docs/books/curriculum-master.html:500", "shortDescription": {"text": "Insecure pattern 'insert_adjacent_html' in docs/books/curriculum-master.html:500"}, "fullDescription": {"text": "Found a known-risky pattern (insert_adjacent_html). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-05a6acfc4a9cca39", "name": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master-ko.html:388", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master-ko.html:388"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f7b356fd0ce5a138", "name": "Insecure pattern 'node_child_process' in scripts/synthesize-game-sfx.mjs:9", "shortDescription": {"text": "Insecure pattern 'node_child_process' in scripts/synthesize-game-sfx.mjs:9"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f549030b17038831", "name": "Insecure pattern 'node_child_process' in scripts/synthesize-runner-sfx.mjs:7", "shortDescription": {"text": "Insecure pattern 'node_child_process' in scripts/synthesize-runner-sfx.mjs:7"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2c35cf40fd528d68", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/design-system.html:566", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/design-system.html:566"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a5bb615d8263d61d", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master.html:426", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master.html:426"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-808d7ecabebbe0fb", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/curriculum-master.html:457", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/curriculum-master.html:457"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b7a73b7b777cebc9", "name": "Insecure pattern 'insert_adjacent_html' in packages/client/public/curriculum-master.html:500", "shortDescription": {"text": "Insecure pattern 'insert_adjacent_html' in packages/client/public/curriculum-master.html:500"}, "fullDescription": {"text": "Found a known-risky pattern (insert_adjacent_html). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-fad127e6c8426587", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master-ko.html:388", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master-ko.html:388"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-475289d0b31e6ffc", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-table-ko.html:397", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-table-ko.html:397"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-18af250cd451a055", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/key-object-editor.html:232", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/key-object-editor.html:232"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-abde877323fc5e7c", "name": "Insecure pattern 'exec_used' in packages/client/src/components/RichTextEditor.tsx:134", "shortDescription": {"text": "Insecure pattern 'exec_used' in packages/client/src/components/RichTextEditor.tsx:134"}, "fullDescription": {"text": "Found a known-risky pattern (exec_used). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-0c3ca11419710b4c", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/components/RichTextEditor.tsx:106", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/components/RichTextEditor.tsx:106"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e5d7c4a62bbb5164", "name": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:99", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:99"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-44e4942c454fb259", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:58", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:58"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1e2ff91d9336f4ac", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/games/components/players/EnglishBlockPlay", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/games/components/players/EnglishBlockPlayer.tsx:52"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f506915d452517f7", "name": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.t", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:437"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f99c3326a1cb1577", "name": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/BlogPreviewDialog.ts", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/BlogPreviewDialog.tsx:155"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4e21c8cd9b2a77ea", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/BlogCardItem", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/BlogCardItem.tsx:23"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-39295dc2ede41f35", "name": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/ChannelTranslationVi", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/ChannelTranslationView.tsx:43"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-cd8754d4b13449f1", "name": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/__tests__/Bl", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/__tests__/BlogCardItem.formatForMobile.test.tsx:10"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-655fb1745c2ffe3a", "name": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx:11", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx:117"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-96e34e0bad9599ce", "name": "Insecure pattern 'private_key_in_repo' in packages/client/src/features/marketing/components/project/sections/FunnelAnaly", "shortDescription": {"text": "Insecure pattern 'private_key_in_repo' in packages/client/src/features/marketing/components/project/sections/FunnelAnalyticsSection.tsx:182"}, "fullDescription": {"text": "Found a known-risky pattern (private_key_in_repo). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "critical", "confidence": 1.0}}, {"id": "scanner-7772cebe56f38d7d", "name": "Insecure pattern 'domparser_html_parse' in packages/client/src/features/marketing/lib/strategy-html-parser.ts:93", "shortDescription": {"text": "Insecure pattern 'domparser_html_parse' in packages/client/src/features/marketing/lib/strategy-html-parser.ts:93"}, "fullDescription": {"text": "Found a known-risky pattern (domparser_html_parse). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-00282ebc72334c7d", "name": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:26", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:26"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-337f03dc810f252a", "name": "Insecure pattern 'node_child_process' in packages/server/scripts/strip-checkerboard-bg.mjs:6", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/strip-checkerboard-bg.mjs:6"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2f28dfbcbb8aa015", "name": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-png-to-webp.mjs:10", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-png-to-webp.mjs:10"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e6cd86a0374366f9", "name": "Insecure pattern 'node_child_process' in packages/server/scripts/normalize-bgm-volume.mjs:18", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/normalize-bgm-volume.mjs:18"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-14203f21f6f5a043", "name": "Insecure pattern 'node_child_process' in packages/server/scripts/merge-and-apply-batch.mjs:6", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/merge-and-apply-batch.mjs:6"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-bfd123e4f862656f", "name": "Insecure pattern 'node_child_process' in packages/server/src/utils/transcode.ts:2", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/utils/transcode.ts:2"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d9be0d62416002f6", "name": "Insecure pattern 'node_child_process' in packages/server/src/utils/audio-duration.ts:5", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/utils/audio-duration.ts:5"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b12d43d3fe1edb8e", "name": "Insecure pattern 'node_child_process' in packages/server/src/providers/longform.provider.ts:1", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/providers/longform.provider.ts:1"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-020aad46d9d6e153", "name": "Insecure pattern 'node_child_process' in packages/server/src/services/book-v2.service.ts:67", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/book-v2.service.ts:67"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-11c7cabad7383f97", "name": "Insecure pattern 'new_function_used' in packages/server/src/services/book-v2-curriculum-seed.service.ts:71", "shortDescription": {"text": "Insecure pattern 'new_function_used' in packages/server/src/services/book-v2-curriculum-seed.service.ts:71"}, "fullDescription": {"text": "Found a known-risky pattern (new_function_used). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-70e4fb685e388105", "name": "Insecure pattern 'node_child_process' in packages/server/src/services/phonics-library.service.ts:14", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/phonics-library.service.ts:14"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-106f09b8940e6b76", "name": "Insecure pattern 'node_child_process' in packages/server/src/services/longform.service.ts:4", "shortDescription": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/longform.service.ts:4"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-abca4ea64c23d07a", "name": "Insecure pattern 'cors_wildcard' in supabase/functions/reset-pin/index.ts:15", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in supabase/functions/reset-pin/index.ts:15"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-cb639d9a10a47b23", "name": "package.json defines install-time lifecycle scripts", "shortDescription": {"text": "package.json defines install-time lifecycle scripts"}, "fullDescription": {"text": "preinstall/install/postinstall/prepare scripts execute during dependency installation. Review them carefully for network calls, obfuscation, shell execution, or credential access."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4601e3ad3bb28677", "name": "No CI/CD pipelines detected", "shortDescription": {"text": "No CI/CD pipelines detected"}, "fullDescription": {"text": "No GitHub Actions, GitLab CI, or CircleCI configs found. Without CI you can't gate deploys on tests/lints."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-89bd408acbba45d5", "name": "Very large file: packages/client/src/features/arcade-games/hori-run/scenes/GameScene.ts (1717 lines)", "shortDescription": {"text": "Very large file: packages/client/src/features/arcade-games/hori-run/scenes/GameScene.ts (1717 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-271bd2e0da0373d7", "name": "Very large file: packages/client/src/features/marketing/components/content/CardNewsPanel.tsx (1285 lines)", "shortDescription": {"text": "Very large file: packages/client/src/features/marketing/components/content/CardNewsPanel.tsx (1285 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8b6ae7fe2600365b", "name": "Very large file: packages/client/src/features/longform-video/components/RenderStep.tsx (1574 lines)", "shortDescription": {"text": "Very large file: packages/client/src/features/longform-video/components/RenderStep.tsx (1574 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-a3d7f5ccd1e97e0d", "name": "Very large file: packages/client/src/features/audiobook/components/AudiobookProjectCard.tsx (1896 lines)", "shortDescription": {"text": "Very large file: packages/client/src/features/audiobook/components/AudiobookProjectCard.tsx (1896 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1a48c513401e6a03", "name": "Very large file: packages/shared/src/constants/index.ts (1737 lines)", "shortDescription": {"text": "Very large file: packages/shared/src/constants/index.ts (1737 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fa31210f41dc116e", "name": "Very large file: packages/server/src/services/book-v2.service.ts (2065 lines)", "shortDescription": {"text": "Very large file: packages/server/src/services/book-v2.service.ts (2065 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-80a6bc9f852a87e7", "name": "Very large file: packages/server/src/services/longform.service.ts (1717 lines)", "shortDescription": {"text": "Very large file: packages/server/src/services/longform.service.ts (1717 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ea3b5e389d8c9c0f", "name": "Low test-to-source ratio", "shortDescription": {"text": "Low test-to-source ratio"}, "fullDescription": {"text": "115 tests / 988 src (ratio 0.12)."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-141b30a41e03817b", "name": "No license file detected", "shortDescription": {"text": "No license file detected"}, "fullDescription": {"text": "No LICENSE/COPYING/NOTICE file was found. Generated repositories often omit licensing, which blocks reuse and automated intake."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-01f96f71d9907a8c", "name": "README lacks setup or run instructions", "shortDescription": {"text": "README lacks setup or run instructions"}, "fullDescription": {"text": "A README exists, but it does not contain common install/setup/run markers. This matches a frequent generated-code pattern: UI is present, operational handoff is thin."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f1f0c52db7c97cef", "name": "Node manifest has dependencies but no lockfile: packages/client/package.json", "shortDescription": {"text": "Node manifest has dependencies but no lockfile: packages/client/package.json"}, "fullDescription": {"text": "`package.json` declares dependencies, but no same-directory npm/pnpm/yarn/bun lockfile was found. Generated projects without lockfiles are less reproducible and harder to secure-scan precisely."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2699d5f16ae11282", "name": "Node manifest has dependencies but no lockfile: packages/shared/package.json", "shortDescription": {"text": "Node manifest has dependencies but no lockfile: packages/shared/package.json"}, "fullDescription": {"text": "`package.json` declares dependencies, but no same-directory npm/pnpm/yarn/bun lockfile was found. Generated projects without lockfiles are less reproducible and harder to secure-scan precisely."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-dd7164eb7a3ac055", "name": "Node manifest has dependencies but no lockfile: packages/remotion/package.json", "shortDescription": {"text": "Node manifest has dependencies but no lockfile: packages/remotion/package.json"}, "fullDescription": {"text": "`package.json` declares dependencies, but no same-directory npm/pnpm/yarn/bun lockfile was found. Generated projects without lockfiles are less reproducible and harder to secure-scan precisely."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b739f2f2be1d714d", "name": "Node manifest has dependencies but no lockfile: packages/server/package.json", "shortDescription": {"text": "Node manifest has dependencies but no lockfile: packages/server/package.json"}, "fullDescription": {"text": "`package.json` declares dependencies, but no same-directory npm/pnpm/yarn/bun lockfile was found. Generated projects without lockfiles are less reproducible and harder to secure-scan precisely."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 1133 console/debugger/print-style debug statements in non-test source. This is a common fast-generation residue before production cleanup."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-72b2a6250083a784", "name": "Placeholder or mock-heavy implementation detected", "shortDescription": {"text": "Placeholder or mock-heavy implementation detected"}, "fullDescription": {"text": "Found 284 placeholder/mock markers across 105 source files. This often means the repo looks complete while core flows still use generated scaffolding or fake data."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-9d79c4077342a7d0", "name": "Runtime service client appears to use placeholder configuration", "shortDescription": {"text": "Runtime service client appears to use placeholder configuration"}, "fullDescription": {"text": "A runtime source file appears to wire Supabase/Firebase/AI/payment-style clients to placeholder URLs, keys, or fallback values. In the Fable corpus this often means the UI/API shape is present while the backend service is not actually configured."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2d0c7b7ab8f8aacf", "name": "Critical user flow still appears backed by mock or placeholder data", "shortDescription": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "fullDescription": {"text": "A payment/auth/admin/order/billing-style flow contains mock, fake, TODO, dummy, or placeholder markers in runtime source. In the Fable corpus this is a high-leverage completeness smell: the app can look finished while the money, identity, or tenant flow is still scaffolded."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b9088664ace7f748", "name": "Composite production-readiness gap", "shortDescription": {"text": "Composite production-readiness gap"}, "fullDescription": {"text": "Multiple low-cost hardening controls are missing together: license, ci, operator-readme, lockfile. Opus verification showed these co-occurring gaps are a better readiness signal than reading each flag in isolation."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-749d4bc1bd66df5f", "name": "Agent instructions exist but release-hardening basics are missing", "shortDescription": {"text": "Agent instructions exist but release-hardening basics are missing"}, "fullDescription": {"text": "AI-coder instruction files were found, but the repo is missing license, ci, operator-readme, lockfile. Treat this as a contract gap: the agent is guided, but the generated output is not yet guarded by the controls that make it repeatable."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ea8f3013f588db25", "name": "Shallow git history limits provenance confidence", "shortDescription": {"text": "Shallow git history limits provenance confidence"}, "fullDescription": {"text": "The repository is a shallow clone. Origin/evolution analysis cannot distinguish fresh generation, imported legacy code, or long-lived human code with high confidence."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8424db9c75e04ba4", "name": "Very short observed git history", "shortDescription": {"text": "Very short observed git history"}, "fullDescription": {"text": "The repo has multiple source files but two or fewer visible commits. This is not a failure by itself, but it lowers confidence in evolution-based diagnosis."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5ae453a25c37091f", "name": "Agent authority lacks a verifier contract: packages/client/src/features/arcade-games/CLAUDE.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/arcade-games/CLAUDE.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d8282f1d7e8b64b9", "name": "Agent instruction/config may expose a secret: packages/client/src/features/marketing/CLAUDE.md", "shortDescription": {"text": "Agent instruction/config may expose a secret: packages/client/src/features/marketing/CLAUDE.md"}, "fullDescription": {"text": "Agent-facing files are routinely pasted into LLM/tool contexts. Move literal tokens, keys, and passwords into a secret manager or document them as placeholders only."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f2a28dec14a4613e", "name": "Agent authority lacks a verifier contract: packages/client/src/features/marketing/CLAUDE.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/marketing/CLAUDE.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-172ae6d897eed928", "name": "Agent authority lacks a verifier contract: packages/client/src/features/longform-video/CLAUDE.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/longform-video/CLAUDE.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-24c71aec70549a7c", "name": "Agent authority lacks a verifier contract: packages/client/src/features/rewards/CLAUDE.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/rewards/CLAUDE.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f0fa2986a2d92926", "name": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-keyword-research.mjs:49", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-keyword-research.mjs:49"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b92db8caef046e08", "name": "Commented-code block (8 lines) in docs/marketing/scripts/audit-noise-and-mine.mjs:17", "shortDescription": {"text": "Commented-code block (8 lines) in docs/marketing/scripts/audit-noise-and-mine.mjs:17"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f6bf536832fdb6d8", "name": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/dataforseo-keyword-research.mjs:186", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/dataforseo-keyword-research.mjs:186"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8048a0cf59e4dc05", "name": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-content-keyword-research.mjs:58", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-content-keyword-research.mjs:58"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-683bd04d1e15b6fa", "name": "Commented-code block (9 lines) in docs/marketing/scripts/consolidate-keywords.mjs:2", "shortDescription": {"text": "Commented-code block (9 lines) in docs/marketing/scripts/consolidate-keywords.mjs:2"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-6a91c5948e1ff27e", "name": "Commented-code block (5 lines) in scripts/generate-pixar-3d-jack.mjs:1", "shortDescription": {"text": "Commented-code block (5 lines) in scripts/generate-pixar-3d-jack.mjs:1"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3af3fde10e323988", "name": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/generate-pixar-3d-jack.mjs:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/generate-pixar-3d-jack.mjs:15"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ed9c5df4add4f7f0", "name": "Commented-code block (5 lines) in scripts/bulk-setup-complete-books.mjs:1", "shortDescription": {"text": "Commented-code block (5 lines) in scripts/bulk-setup-complete-books.mjs:1"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-e03f14eba8ab095d", "name": "Commented-code block (10 lines) in scripts/audit-v1-storybooks.mjs:3", "shortDescription": {"text": "Commented-code block (10 lines) in scripts/audit-v1-storybooks.mjs:3"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-2821ed6d8d83e77c", "name": "Commented-code block (7 lines) in scripts/seed-phonics-books.mjs:12", "shortDescription": {"text": "Commented-code block (7 lines) in scripts/seed-phonics-books.mjs:12"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-94b552b51415c6ad", "name": "Commented-code block (14 lines) in scripts/migrate-vocabulary-to-keyobjects.mjs:3", "shortDescription": {"text": "Commented-code block (14 lines) in scripts/migrate-vocabulary-to-keyobjects.mjs:3"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f0e0a9e0b4b61184", "name": "Commented-code block (16 lines) in scripts/fill-reading-level.mjs:3", "shortDescription": {"text": "Commented-code block (16 lines) in scripts/fill-reading-level.mjs:3"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c1bd985450c734bf", "name": "Commented-code block (5 lines) in scripts/migrate-korean-phonics-words.mjs:7", "shortDescription": {"text": "Commented-code block (5 lines) in scripts/migrate-korean-phonics-words.mjs:7"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3aca6a4fd5a63c44", "name": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/seed-hori-stub.mjs:40", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/seed-hori-stub.mjs:40"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e73d8f51c1e6c05e", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a90c554873f123a7", "name": "Commented-code block (5 lines) in packages/client/src/pages/BookDetailPage.tsx:169", "shortDescription": {"text": "Commented-code block (5 lines) in packages/client/src/pages/BookDetailPage.tsx:169"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-fde5988f1ed7583b", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/hooks/useAssetPreloadProgress.ts:18", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/hooks/useAssetPreloadProgress.ts:18"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1874586782b986ee", "name": "Commented-code block (6 lines) in packages/client/src/features/phonics-learner/components/KoreanPhonicsActivityPage.tsx:", "shortDescription": {"text": "Commented-code block (6 lines) in packages/client/src/features/phonics-learner/components/KoreanPhonicsActivityPage.tsx:139"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5b8d5b47b8903874", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibr", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibrary.ts:14"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-831ac1ea3f8e1ec6", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.ts:12"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4696b84ca8a92aa8", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ef3c3e76ac945633", "name": "Commented-code block (5 lines) in packages/client/src/features/games/hooks/usePhonicsMap.ts:7", "shortDescription": {"text": "Commented-code block (5 lines) in packages/client/src/features/games/hooks/usePhonicsMap.ts:7"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-57118c65ad52a841", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/usePhonicsMap.ts:77", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/usePhonicsMap.ts:77"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-dc09104ed01a0a55", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/auth/api/auth.api.ts:59", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/auth/api/auth.api.ts:59"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b803744e861d6eb4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:52", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:52"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-c3e3b930a9a169f2", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-strategy-templates.ts:14", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-strategy-templates.ts:14"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-295711b7e503e6a6", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-r2-upload.ts:10", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-r2-upload.ts:10"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-017b45207d50e9b0", "name": "Commented-code block (5 lines) in packages/client/src/features/marketing/api/use-analytics.ts:14", "shortDescription": {"text": "Commented-code block (5 lines) in packages/client/src/features/marketing/api/use-analytics.ts:14"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-bcd1aad8b98d2cfa", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/strategy/StrategyImportD", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/strategy/StrategyImportDialog.tsx:63"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-15afc0bf7fba2dd8", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboa", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:60"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a19108f3d672f62d", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/hooks/use-channel-translation.ts:28", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/hooks/use-channel-translation.ts:28"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ae85f1a4bf26c490", "name": "Legacy-named symbol `__legacy` in packages/client/src/features/longform-video/lib/group-by-style.ts:3", "shortDescription": {"text": "Legacy-named symbol `__legacy` in packages/client/src/features/longform-video/lib/group-by-style.ts:3"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b373ac64c91675c5", "name": "Commented-code block (5 lines) in packages/client/src/features/phonics/components/LetterWritingCanvas.tsx:297", "shortDescription": {"text": "Commented-code block (5 lines) in packages/client/src/features/phonics/components/LetterWritingCanvas.tsx:297"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-a8a5213b39c7df24", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:94", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:94"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f3cee18d3fd2a1d5", "name": "Commented-code block (5 lines) in packages/server/scripts/cleanup-l4-leftovers.mjs:2", "shortDescription": {"text": "Commented-code block (5 lines) in packages/server/scripts/cleanup-l4-leftovers.mjs:2"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-122324a6cb42687b", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/scan-untranslated-keyobjects.mjs:16", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/scan-untranslated-keyobjects.mjs:16"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e28264fb8aa8237e", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-points-book1.mjs:546", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-points-book1.mjs:546"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d02021f7de7754e6", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/verify-key-object-in-page-text.mjs:35", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/verify-key-object-in-page-text.mjs:35"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-9491c9504c5207fb", "name": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-key-objects-wiktionary.mjs:61", "shortDescription": {"text": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-key-objects-wiktionary.mjs:61"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-155c5881593b0319", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-key-objects-strict-ko.mjs:146", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-key-objects-strict-ko.mjs:146"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-5c0533f26b65d04e", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/sync-phonics-book1-targetwords.mjs:35", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/sync-phonics-book1-targetwords.mjs:35"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-092e453811f86313", "name": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-by-page-text.mjs:81", "shortDescription": {"text": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-by-page-text.mjs:81"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4bf1fa8270322915", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-by-page-text.mjs:188", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-by-page-text.mjs:188"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-02f0853c4c95873a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/backfill-phonics-book1-flashcard-phonemes.mjs:30", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/backfill-phonics-book1-flashcard-phonemes.mjs:30"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a3280609726a3e0a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/research-keyword-plans.mjs:63", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/research-keyword-plans.mjs:63"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-578f9fffbef5994f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-strokes-book1.mjs:354", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-strokes-book1.mjs:354"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f03d372d16062848", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/dump-non-noun-key-objects.mjs:66", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/dump-non-noun-key-objects.mjs:66"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ad0ba1f757f7bf61", "name": "Legacy-named symbol `totalOld` in packages/server/src/scripts/migrate-assets.ts:35", "shortDescription": {"text": "Legacy-named symbol `totalOld` in packages/server/src/scripts/migrate-assets.ts:35"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d9a0d36078d150db", "name": "Commented-code block (18 lines) in packages/server/src/utils/book-v2-keys.ts:3", "shortDescription": {"text": "Commented-code block (18 lines) in packages/server/src/utils/book-v2-keys.ts:3"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-8aca41e24b3c3ca9", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/grok.provider.ts:75", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/grok.provider.ts:75"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-80286bb3eced3afd", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/naver.provider.ts:38", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/naver.provider.ts:38"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d3becfe9d53e8746", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/book-v2.service.ts:323", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/book-v2.service.ts:323"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1ff3eaf6a2c8e044", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/publish.service.ts:79", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/publish.service.ts:79"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4d5652c4d409a756", "name": "Commented-code block (5 lines) in packages/server/src/services/mkt/__tests__/publish-scheduler.service.test.ts:10", "shortDescription": {"text": "Commented-code block (5 lines) in packages/server/src/services/mkt/__tests__/publish-scheduler.service.test.ts:10"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-aecca1e0d4455a23", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/ga4.ts:107", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/ga4.ts:107"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d0cfa37ce098efc7", "name": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/youtube-data.ts:120", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/youtube-data.ts:120"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-cc0f9d8e41b33b0e", "name": "11 env vars used in code but missing from .env.example", "shortDescription": {"text": "11 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `API_BASE`, `CHROMIUM_PATH`, `OPENAI_API_KEY`, `SERVER_URL`, `TEMP`, `VITE_SUPABASE_ANON_KEY`, `VITE_SUPABASE_URL`, `XAI_API_KEY` + 3 more. Add them (with a placeholder/comment) to .env.example so onboarding doesn't break."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fd6ca0aaf987a2e6", "name": "Frontend route `/marketing/*` has no Link/navigate to it \u2014 packages/client/src/features/marketing/pages/MarketingLayout.", "shortDescription": {"text": "Frontend route `/marketing/*` has no Link/navigate to it \u2014 packages/client/src/features/marketing/pages/MarketingLayout.test.tsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1fc47e9321235cd2", "name": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/sc", "shortDescription": {"text": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/scripts/classify-key-objects-wiktionary.mjs:60)"}, "fullDescription": {"text": "`packages/server/scripts/classify-key-objects-wiktionary.mjs:60` calls `GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/en.wiktionary.org/api/rest_v1/page/definition/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-5aab9815412c6708", "name": "Dangling fetch: GET https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(key)} (packages/server/scripts/", "shortDescription": {"text": "Dangling fetch: GET https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(key)} (packages/server/scripts/classify-key-objects-en.mjs:50)"}, "fullDescription": {"text": "`packages/server/scripts/classify-key-objects-en.mjs:50` calls `GET https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(key)}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.dictionaryapi.dev/api/v2/entries/en/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-d719978a4c9306bf", "name": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/sc", "shortDescription": {"text": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/scripts/classify-by-page-text.mjs:80)"}, "fullDescription": {"text": "`packages/server/scripts/classify-by-page-text.mjs:80` calls `GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/en.wiktionary.org/api/rest_v1/page/definition/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-83a227179cd30fa1", "name": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/scripts/rese", "shortDescription": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/scripts/research-keyword-plans.mjs:63)"}, "fullDescription": {"text": "`packages/server/scripts/research-keyword-plans.mjs:63` calls `POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.dataforseo.com/v3/keywords_data/google/search_volume/live`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-eeae7092e14026fd", "name": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:70)", "shortDescription": {"text": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:70)"}, "fullDescription": {"text": "`packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:70` calls `GET /api/korean-jamo-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/korean-jamo-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-20979a6a2bce6be7", "name": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:146)", "shortDescription": {"text": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:146)"}, "fullDescription": {"text": "`packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:146` calls `PUT /api/korean-jamo-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/korean-jamo-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-1417cb44c17a9f57", "name": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272)", "shortDescription": {"text": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272)"}, "fullDescription": {"text": "`packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272` calls `PUT /api/korean-jamo-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/korean-jamo-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-59d910b3f44cb79b", "name": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:63)", "shortDescription": {"text": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:63)"}, "fullDescription": {"text": "`packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:63` calls `GET /api/letter-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/letter-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-daa6ffcd69ec72a2", "name": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:190)", "shortDescription": {"text": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:190)"}, "fullDescription": {"text": "`packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:190` calls `PUT /api/letter-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/letter-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-bcc24d748c28d538", "name": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:311)", "shortDescription": {"text": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:311)"}, "fullDescription": {"text": "`packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:311` calls `PUT /api/letter-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/letter-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-a4277bbd9e7fe714", "name": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/features/phonics-learner/hooks/useKoreanJamoStr", "shortDescription": {"text": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibrary.ts:14)"}, "fullDescription": {"text": "`packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibrary.ts:14` calls `GET /api/korean-jamo-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/korean-jamo-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-3fe3ffbebbbe7a32", "name": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrar", "shortDescription": {"text": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.ts:12)"}, "fullDescription": {"text": "`packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.ts:12` calls `GET /api/letter-stroke-library` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/letter-stroke-library`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-e132bd289bcba02b", "name": "Dangling fetch: POST /api/speaking/transcribe (packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202)", "shortDescription": {"text": "Dangling fetch: POST /api/speaking/transcribe (packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202)"}, "fullDescription": {"text": "`packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202` calls `POST /api/speaking/transcribe` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/speaking/transcribe`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-dbcf45c1e31bf6ab", "name": "Dangling fetch: POST /api/mkt/keywords/recommend (packages/client/src/features/marketing/api/use-golden-keywords.ts:25)", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/keywords/recommend (packages/client/src/features/marketing/api/use-golden-keywords.ts:25)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/api/use-golden-keywords.ts:25` calls `POST /api/mkt/keywords/recommend` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/keywords/recommend`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-9521849c87ae1658", "name": "Dangling fetch: GET /api/mkt/strategy/templates (packages/client/src/features/marketing/api/use-strategy-templates.ts:14", "shortDescription": {"text": "Dangling fetch: GET /api/mkt/strategy/templates (packages/client/src/features/marketing/api/use-strategy-templates.ts:14)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/api/use-strategy-templates.ts:14` calls `GET /api/mkt/strategy/templates` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/strategy/templates`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-3a6a7e10e63d499e", "name": "Dangling fetch: POST /api/mkt/storage/presign (packages/client/src/features/marketing/api/use-r2-upload.ts:36)", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/storage/presign (packages/client/src/features/marketing/api/use-r2-upload.ts:36)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/api/use-r2-upload.ts:36` calls `POST /api/mkt/storage/presign` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/storage/presign`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-8440d7b599d77a38", "name": "Dangling fetch: POST /api/mkt/analytics/meta-insights (packages/client/src/features/marketing/api/use-analytics.ts:163)", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/analytics/meta-insights (packages/client/src/features/marketing/api/use-analytics.ts:163)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/api/use-analytics.ts:163` calls `POST /api/mkt/analytics/meta-insights` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/analytics/meta-insights`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-26a875f5c9c807dd", "name": "Dangling fetch: POST /api/mkt/analytics/youtube-channel (packages/client/src/features/marketing/api/use-analytics.ts:224", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/analytics/youtube-channel (packages/client/src/features/marketing/api/use-analytics.ts:224)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/api/use-analytics.ts:224` calls `POST /api/mkt/analytics/youtube-channel` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/analytics/youtube-channel`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f2c00e39d3b1f053", "name": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/components/content/BaseArticlePanel.ts", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/components/content/BaseArticlePanel.tsx:133)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/components/content/BaseArticlePanel.tsx:133` calls `POST /api/mkt/ai/generate` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/generate`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-1d2ac1047772f82b", "name": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/BgmSe", "shortDescription": {"text": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/BgmSection.tsx:79)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/components/project/sections/BgmSection.tsx:79` calls `DELETE /api/mkt/storage/delete` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/storage/delete`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-62f793eb15b5d89b", "name": "Dangling fetch: POST /api/mkt/ai/extract-text (packages/client/src/features/marketing/components/project/sections/Refere", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/extract-text (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:71)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:71` calls `POST /api/mkt/ai/extract-text` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/extract-text`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-82c18527c2a14dc2", "name": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/Refer", "shortDescription": {"text": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:118)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:118` calls `DELETE /api/mkt/storage/delete` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/storage/delete`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-993cf9f84f576c45", "name": "Dangling fetch: POST /api/mkt/ai/analyze-references (packages/client/src/features/marketing/components/project/sections/", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/analyze-references (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:141)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:141` calls `POST /api/mkt/ai/analyze-references` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/analyze-references`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-789df64cb5c20f9a", "name": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/hooks/use-ai-generation.ts:23)", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/hooks/use-ai-generation.ts:23)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/hooks/use-ai-generation.ts:23` calls `POST /api/mkt/ai/generate` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/generate`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-27bbe76405eab5b7", "name": "Dangling fetch: GET /api/mkt/storage/proxy?url=${encodeURIComponent(url)} (packages/client/src/features/marketing/hooks/", "shortDescription": {"text": "Dangling fetch: GET /api/mkt/storage/proxy?url=${encodeURIComponent(url)} (packages/client/src/features/marketing/hooks/use-channel-translation.ts:28)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/hooks/use-channel-translation.ts:28` calls `GET /api/mkt/storage/proxy?url=${encodeURIComponent(url)}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/storage/proxy`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-ca24559425e01984", "name": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/hooks/use-image-generation.ts:14", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/hooks/use-image-generation.ts:14)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/hooks/use-image-generation.ts:14` calls `POST /api/mkt/ai/generate-image` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/generate-image`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-b8bce4582851e7b7", "name": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/store/batch-image-store.ts:102)", "shortDescription": {"text": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/store/batch-image-store.ts:102)"}, "fullDescription": {"text": "`packages/client/src/features/marketing/store/batch-image-store.ts:102` calls `POST /api/mkt/ai/generate-image` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/mkt/ai/generate-image`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-96c98cb6d3d49399", "name": "Dangling fetch: GET https://api.x.ai/v1/videos/${requestId} (packages/server/src/providers/grok.provider.ts:35)", "shortDescription": {"text": "Dangling fetch: GET https://api.x.ai/v1/videos/${requestId} (packages/server/src/providers/grok.provider.ts:35)"}, "fullDescription": {"text": "`packages/server/src/providers/grok.provider.ts:35` calls `GET https://api.x.ai/v1/videos/${requestId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.x.ai/v1/videos/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-ea923d64a6622dca", "name": "Dangling fetch: POST https://api.x.ai/v1/videos/generations (packages/server/src/providers/grok.provider.ts:126)", "shortDescription": {"text": "Dangling fetch: POST https://api.x.ai/v1/videos/generations (packages/server/src/providers/grok.provider.ts:126)"}, "fullDescription": {"text": "`packages/server/src/providers/grok.provider.ts:126` calls `POST https://api.x.ai/v1/videos/generations` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.x.ai/v1/videos/generations`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-478dae54305529d5", "name": "Dangling fetch: GET https://graph.facebook.com/v21.0/ig_hashtag_search?q=${encodeURIComponent(\n      keyword\n    )}&user", "shortDescription": {"text": "Dangling fetch: GET https://graph.facebook.com/v21.0/ig_hashtag_search?q=${encodeURIComponent(\n      keyword\n    )}&user_id=${encodeURIComponent(igUserId)}&access_token=${accessToken} (packages/server/src/services/mkt/monitoring.service.ts:"}, "fullDescription": {"text": "`packages/server/src/services/mkt/monitoring.service.ts:343` calls `GET https://graph.facebook.com/v21.0/ig_hashtag_search?q=${encodeURIComponent(\n      keyword\n    )}&user_id=${encodeURIComponent(igUserId)}&access_token=${accessToken}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/graph.facebook.com/v21.0/ig_hashtag_search`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-5ec598b427c5c9c1", "name": "Dangling fetch: GET https://graph.facebook.com/v21.0/${encodeURIComponent(\n      hashtagId\n    )}/recent_media?user_id=$", "shortDescription": {"text": "Dangling fetch: GET https://graph.facebook.com/v21.0/${encodeURIComponent(\n      hashtagId\n    )}/recent_media?user_id=${encodeURIComponent(\n      igUserId\n    )}&fields=id,caption,media_type,media_url,permalink,timestamp,like_count,comment"}, "fullDescription": {"text": "`packages/server/src/services/mkt/monitoring.service.ts:353` calls `GET https://graph.facebook.com/v21.0/${encodeURIComponent(\n      hashtagId\n    )}/recent_media?user_id=${encodeURIComponent(\n      igUserId\n    )}&fields=id,caption,media_type,media_url,permalink,timestamp,like_count,comments_count&access_token=${accessToken}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/graph.facebook.com/v21.0/<p>/recent_media`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-34845ec2d6c42166", "name": "Dangling fetch: POST https://oauth2.googleapis.com/token (packages/server/src/services/mkt/external/ga4.ts:107)", "shortDescription": {"text": "Dangling fetch: POST https://oauth2.googleapis.com/token (packages/server/src/services/mkt/external/ga4.ts:107)"}, "fullDescription": {"text": "`packages/server/src/services/mkt/external/ga4.ts:107` calls `POST https://oauth2.googleapis.com/token` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/oauth2.googleapis.com/token`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f455f8956c4bf723", "name": "Dangling fetch: POST https://analyticsdata.googleapis.com/v1beta/properties/${cfg.propertyId}:runReport (packages/server", "shortDescription": {"text": "Dangling fetch: POST https://analyticsdata.googleapis.com/v1beta/properties/${cfg.propertyId}:runReport (packages/server/src/services/mkt/external/ga4.ts:131)"}, "fullDescription": {"text": "`packages/server/src/services/mkt/external/ga4.ts:131` calls `POST https://analyticsdata.googleapis.com/v1beta/properties/${cfg.propertyId}:runReport` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/analyticsdata.googleapis.com/v1beta/properties/<p>/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-9499afc569e06976", "name": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/src/services", "shortDescription": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/src/services/mkt/external/dataforseo.ts:95)"}, "fullDescription": {"text": "`packages/server/src/services/mkt/external/dataforseo.ts:95` calls `POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.dataforseo.com/v3/keywords_data/google/search_volume/live`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-39ddc19c9740ec33", "name": "Dangling fetch: POST https://api.dataforseo.com/v3/serp/google/organic/live/advanced (packages/server/src/services/mkt/e", "shortDescription": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/serp/google/organic/live/advanced (packages/server/src/services/mkt/external/dataforseo.ts:130)"}, "fullDescription": {"text": "`packages/server/src/services/mkt/external/dataforseo.ts:130` calls `POST https://api.dataforseo.com/v3/serp/google/organic/live/advanced` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.dataforseo.com/v3/serp/google/organic/live/advanced`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-e27de824cf0f3ca2", "name": "Unused endpoint: POST /images/analyze-style", "shortDescription": {"text": "Unused endpoint: POST /images/analyze-style"}, "fullDescription": {"text": "`packages/client/src/features/settings/api/settings.api.ts` declares `POST /images/analyze-style` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9abaa7f794c14ad6", "name": "Unused endpoint: POST /images/upload-audio", "shortDescription": {"text": "Unused endpoint: POST /images/upload-audio"}, "fullDescription": {"text": "`packages/client/src/features/settings/api/settings.api.ts` declares `POST /images/upload-audio` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-586532bd75938b2d", "name": "Unused endpoint: POST /phonics-library/upload", "shortDescription": {"text": "Unused endpoint: POST /phonics-library/upload"}, "fullDescription": {"text": "`packages/client/src/features/settings/api/settings.api.ts` declares `POST /phonics-library/upload` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6c648649b8c303d9", "name": "Unused endpoint: POST /system-sounds/upload", "shortDescription": {"text": "Unused endpoint: POST /system-sounds/upload"}, "fullDescription": {"text": "`packages/client/src/features/settings/api/settings.api.ts` declares `POST /system-sounds/upload` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d0a2eeada7bcb08c", "name": "Unused endpoint: POST /images/download-zip", "shortDescription": {"text": "Unused endpoint: POST /images/download-zip"}, "fullDescription": {"text": "`packages/client/src/features/illustration/components/PagesTab.tsx` declares `POST /images/download-zip` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8ab40b02f926b71c", "name": "Unused endpoint: GET /api/debug/logs", "shortDescription": {"text": "Unused endpoint: GET /api/debug/logs"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `GET /api/debug/logs` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5b04b741ddbc5ce9", "name": "Unused endpoint: USE /api/storybooks", "shortDescription": {"text": "Unused endpoint: USE /api/storybooks"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/storybooks` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5da77a419e1bf1c4", "name": "Unused endpoint: USE /api/images", "shortDescription": {"text": "Unused endpoint: USE /api/images"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/images` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-54922513d7dd0abe", "name": "Unused endpoint: USE /api/tts", "shortDescription": {"text": "Unused endpoint: USE /api/tts"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/tts` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-48e1f815b5fc2c5c", "name": "Unused endpoint: USE /api/translation", "shortDescription": {"text": "Unused endpoint: USE /api/translation"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/translation` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-70c90579a534e096", "name": "Unused endpoint: USE /api/quiz", "shortDescription": {"text": "Unused endpoint: USE /api/quiz"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/quiz` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1a1fd97cebed4b2f", "name": "Unused endpoint: USE /api/audiobooks", "shortDescription": {"text": "Unused endpoint: USE /api/audiobooks"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/audiobooks` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-67d8b39db0f89cc2", "name": "Unused endpoint: USE /api/phonics", "shortDescription": {"text": "Unused endpoint: USE /api/phonics"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/phonics` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-bfa15851ef1babd4", "name": "Unused endpoint: USE /api/games", "shortDescription": {"text": "Unused endpoint: USE /api/games"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/games` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-a6afd11065d1a0fc", "name": "Unused endpoint: USE /api/phonics-library", "shortDescription": {"text": "Unused endpoint: USE /api/phonics-library"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/phonics-library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8ca4664c9cda1cd6", "name": "Unused endpoint: USE /api/settings", "shortDescription": {"text": "Unused endpoint: USE /api/settings"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/settings` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-89d9a579767e69a2", "name": "Unused endpoint: USE /api/system-sounds", "shortDescription": {"text": "Unused endpoint: USE /api/system-sounds"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/system-sounds` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-110ec2983974870e", "name": "Unused endpoint: USE /api/character-library", "shortDescription": {"text": "Unused endpoint: USE /api/character-library"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/character-library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c99484be99f591ea", "name": "Unused endpoint: USE /api/art-style-library", "shortDescription": {"text": "Unused endpoint: USE /api/art-style-library"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/art-style-library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9e75e50a0913576a", "name": "Unused endpoint: USE /api/vocabulary-db", "shortDescription": {"text": "Unused endpoint: USE /api/vocabulary-db"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/vocabulary-db` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4d2feab9ea8ddbb8", "name": "Unused endpoint: USE /api/marketing", "shortDescription": {"text": "Unused endpoint: USE /api/marketing"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/marketing` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0e430ecbee20ddd9", "name": "Unused endpoint: USE /api/prompt-presets", "shortDescription": {"text": "Unused endpoint: USE /api/prompt-presets"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/prompt-presets` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b8bff7d9e91a6fbc", "name": "Unused endpoint: USE /api/longform", "shortDescription": {"text": "Unused endpoint: USE /api/longform"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/longform` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e489bcb6d411b75b", "name": "Unused endpoint: USE /api/youtube-presets", "shortDescription": {"text": "Unused endpoint: USE /api/youtube-presets"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/youtube-presets` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0a202014de24cd71", "name": "Unused endpoint: USE /api/speaking", "shortDescription": {"text": "Unused endpoint: USE /api/speaking"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/speaking` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6d33b31ec8c97317", "name": "Unused endpoint: USE /api/admin/book-v2", "shortDescription": {"text": "Unused endpoint: USE /api/admin/book-v2"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/admin/book-v2` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3da6ece45fff7553", "name": "Unused endpoint: USE /api/v2/books", "shortDescription": {"text": "Unused endpoint: USE /api/v2/books"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/v2/books` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-906072993257effe", "name": "Unused endpoint: USE /api/hori", "shortDescription": {"text": "Unused endpoint: USE /api/hori"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/hori` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5e8f238aa42558c5", "name": "Unused endpoint: USE /api/playground", "shortDescription": {"text": "Unused endpoint: USE /api/playground"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/playground` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-cc1c4a8ac4ffe02a", "name": "Unused endpoint: USE /api/vocabulary-units", "shortDescription": {"text": "Unused endpoint: USE /api/vocabulary-units"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/vocabulary-units` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-417f2248ee9ad120", "name": "Unused endpoint: USE /api/library-config", "shortDescription": {"text": "Unused endpoint: USE /api/library-config"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/library-config` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b991bde539207c9b", "name": "Unused endpoint: USE /api/vocab-overrides", "shortDescription": {"text": "Unused endpoint: USE /api/vocab-overrides"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/vocab-overrides` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-135b1b5d467bf8ec", "name": "Unused endpoint: USE /api/letter-stroke-library", "shortDescription": {"text": "Unused endpoint: USE /api/letter-stroke-library"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/letter-stroke-library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-cfd0c4c99d7b0237", "name": "Unused endpoint: USE /api/korean-jamo-stroke-library", "shortDescription": {"text": "Unused endpoint: USE /api/korean-jamo-stroke-library"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/korean-jamo-stroke-library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e4dd14116a017b24", "name": "Unused endpoint: USE /api/mkt", "shortDescription": {"text": "Unused endpoint: USE /api/mkt"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `USE /api/mkt` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f0e081a44d033b2a", "name": "Unused endpoint: GET /api/r2-proxy", "shortDescription": {"text": "Unused endpoint: GET /api/r2-proxy"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `GET /api/r2-proxy` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-77a21e69b379a5bf", "name": "Unused endpoint: GET /{*path}", "shortDescription": {"text": "Unused endpoint: GET /{*path}"}, "fullDescription": {"text": "`packages/server/src/app.ts` declares `GET /{*path}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-34a0d470c601d97f", "name": "Unused endpoint: GET /catalog", "shortDescription": {"text": "Unused endpoint: GET /catalog"}, "fullDescription": {"text": "`packages/server/src/routes/hori.routes.ts` declares `GET /catalog` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7e2aebb8dfde31c6", "name": "Unused endpoint: POST /items", "shortDescription": {"text": "Unused endpoint: POST /items"}, "fullDescription": {"text": "`packages/server/src/routes/hori.routes.ts` declares `POST /items` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5baa8971ebe192a1", "name": "Unused endpoint: GET /", "shortDescription": {"text": "Unused endpoint: GET /"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `GET /` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e305f58e0a58346e", "name": "Unused endpoint: GET /word/:word", "shortDescription": {"text": "Unused endpoint: GET /word/:word"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `GET /word/:word` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-97602644d814e467", "name": "Unused endpoint: GET /storybook/:storybookId", "shortDescription": {"text": "Unused endpoint: GET /storybook/:storybookId"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `GET /storybook/:storybookId` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fa413208496243a5", "name": "Unused endpoint: POST /sync", "shortDescription": {"text": "Unused endpoint: POST /sync"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `POST /sync` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0fba7734c9f573f3", "name": "Unused endpoint: POST /bulk-sync", "shortDescription": {"text": "Unused endpoint: POST /bulk-sync"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `POST /bulk-sync` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0a71b65913e31f1b", "name": "Unused endpoint: DELETE /reset", "shortDescription": {"text": "Unused endpoint: DELETE /reset"}, "fullDescription": {"text": "`packages/server/src/routes/vocabulary-db.routes.ts` declares `DELETE /reset` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fd2c7e47d6c67bab", "name": "Unused endpoint: POST /word-pool/enrich", "shortDescription": {"text": "Unused endpoint: POST /word-pool/enrich"}, "fullDescription": {"text": "`packages/server/src/routes/playground.routes.ts` declares `POST /word-pool/enrich` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-52362b8f59ba18c0", "name": "Unused endpoint: GET /word-pool/sample", "shortDescription": {"text": "Unused endpoint: GET /word-pool/sample"}, "fullDescription": {"text": "`packages/server/src/routes/playground.routes.ts` declares `GET /word-pool/sample` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5e30f8198fa3c7b0", "name": "Unused endpoint: GET /word-pool/by-categories", "shortDescription": {"text": "Unused endpoint: GET /word-pool/by-categories"}, "fullDescription": {"text": "`packages/server/src/routes/playground.routes.ts` declares `GET /word-pool/by-categories` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-03539564056e5279", "name": "Unused endpoint: POST /blog/generate", "shortDescription": {"text": "Unused endpoint: POST /blog/generate"}, "fullDescription": {"text": "`packages/server/src/routes/marketing.routes.ts` declares `POST /blog/generate` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-59d7c3251efeaba8", "name": "Unused endpoint: POST /blog/generate-config", "shortDescription": {"text": "Unused endpoint: POST /blog/generate-config"}, "fullDescription": {"text": "`packages/server/src/routes/marketing.routes.ts` declares `POST /blog/generate-config` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}]}}, "automationDetails": {"id": "repobility/22815"}, "properties": {"repository": "wisdomproto/tangobook", "repoUrl": "https://github.com/wisdomproto/tangobook", "branch": "main"}, "results": [{"ruleId": "scanner-27bcaecf13b7f79a", "level": "note", "message": {"text": "Possibly dead Python function: download_file"}, "properties": {"repobilityId": "9d5d70ce1b4ee0b7", "scanner": "scanner-primary", "fingerprint": "27bcaecf13b7f79a", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/generate_longform.py:55"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-e3ff781abcfa2f80", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/components/AppShell.tsx:142"}, "properties": {"repobilityId": "e70e7396c47a05e6", "scanner": "scanner-primary", "fingerprint": "e3ff781abcfa2f80", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-85bb8fc37d9fa962", "level": "none", "message": {"text": "TODO/FIXME marker in shipping code \u2014 packages/client/src/pages/GamesHubPage.tsx:34"}, "properties": {"repobilityId": "0fb76914a248c8fa", "scanner": "scanner-primary", "fingerprint": "85bb8fc37d9fa962", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.todo-marker"]}}, {"ruleId": "scanner-7c563585b4a296ad", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/BookDetailPage.tsx:272"}, "properties": {"repobilityId": "96db7b0dc42244d4", "scanner": "scanner-primary", "fingerprint": "7c563585b4a296ad", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-45b9a1fcc9b4f663", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/pages/LibraryMasterPage.tsx:464"}, "properties": {"repobilityId": "d05bcaf522ca10fa", "scanner": "scanner-primary", "fingerprint": "45b9a1fcc9b4f663", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-59a7bd3f1eec4bb8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/design-system/primitives/PageHeader.tsx:34"}, "properties": {"repobilityId": "66211500cf712a11", "scanner": "scanner-primary", "fingerprint": "59a7bd3f1eec4bb8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a86a339797bd0c5b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SettingsTab.tsx:448"}, "properties": {"repobilityId": "5a66ab02fb4a6b27", "scanner": "scanner-primary", "fingerprint": "a86a339797bd0c5b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0982e744ec5fc8b0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/PhonicsAudioLibrary.tsx:392"}, "properties": {"repobilityId": "99fed62585f861d4", "scanner": "scanner-primary", "fingerprint": "0982e744ec5fc8b0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d2cde09e0a85d9d8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/settings/components/SystemSoundsLibrary.tsx:365"}, "properties": {"repobilityId": "c2b205e64dc947e9", "scanner": "scanner-primary", "fingerprint": "d2cde09e0a85d9d8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-8305ad66b9e0bf33", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/KoreanPhonicsStudyPage.tsx:154"}, "properties": {"repobilityId": "6dd493f62ea386bd", "scanner": "scanner-primary", "fingerprint": "8305ad66b9e0bf33", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-810e66e0901f2bdf", "level": "note", "message": {"text": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/KoreanPhonicsStudyPage.tsx:244"}, "properties": {"repobilityId": "9110b6b82b69ee60", "scanner": "scanner-primary", "fingerprint": "810e66e0901f2bdf", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.active-light-bg"]}}, {"ruleId": "scanner-aa209194bd100619", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics-learner/components/EnglishPhonicsStudyPage.tsx:119"}, "properties": {"repobilityId": "9ad462490392aa06", "scanner": "scanner-primary", "fingerprint": "aa209194bd100619", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ee583d28f1b2b384", "level": "note", "message": {"text": "\"active\" state uses light bg in a dark theme \u2014 packages/client/src/features/phonics-learner/components/EnglishPhonicsStudyPage.tsx:198"}, "properties": {"repobilityId": "f8399888f7bfc14b", "scanner": "scanner-primary", "fingerprint": "ee583d28f1b2b384", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.active-light-bg"]}}, {"ruleId": "scanner-1a727bc8ab4b281c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogPostCard.tsx:139"}, "properties": {"repobilityId": "d92dbaf88114c6a3", "scanner": "scanner-primary", "fingerprint": "1a727bc8ab4b281c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-eb00d251d1271a9c", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:88"}, "properties": {"repobilityId": "f29c6e385b9e7760", "scanner": "scanner-primary", "fingerprint": "eb00d251d1271a9c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-9064123593082bb8", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/blog/components/BlogPreviewModal.tsx:99"}, "properties": {"repobilityId": "f6149062997d06f0", "scanner": "scanner-primary", "fingerprint": "9064123593082bb8", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-fdc3b19ab780f01f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/blog/components/BlogSectionEditor.tsx:209"}, "properties": {"repobilityId": "1d71f83554211d7c", "scanner": "scanner-primary", "fingerprint": "fdc3b19ab780f01f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-cc2257a5da98a3c8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategorySection.tsx:26"}, "properties": {"repobilityId": "98b08852a658ec05", "scanner": "scanner-primary", "fingerprint": "cc2257a5da98a3c8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d7bb3e54483067ec", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookMatrixModal.tsx:260"}, "properties": {"repobilityId": "3d990bf7ae4b949c", "scanner": "scanner-primary", "fingerprint": "d7bb3e54483067ec", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5094852e53390d7e", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCardEditable.tsx:122"}, "properties": {"repobilityId": "a9a80ebd90a8a22c", "scanner": "scanner-primary", "fingerprint": "5094852e53390d7e", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a4b0b591d3c20347", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/BookCard.tsx:48"}, "properties": {"repobilityId": "2951c049046fc431", "scanner": "scanner-primary", "fingerprint": "a4b0b591d3c20347", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b66dbe97f33d3888", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryPanel.tsx:184"}, "properties": {"repobilityId": "09df51819336f9de", "scanner": "scanner-primary", "fingerprint": "b66dbe97f33d3888", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6504cf76cee04537", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/library/components/CategoryChipDropdown.tsx:38"}, "properties": {"repobilityId": "e4458ff826db653e", "scanner": "scanner-primary", "fingerprint": "6504cf76cee04537", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-21713489177efda0", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GamePreviewModal.tsx:43"}, "properties": {"repobilityId": "a8104e40f7fb0672", "scanner": "scanner-primary", "fingerprint": "21713489177efda0", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-62b51054eaaaabda", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/GameCreatorModal.tsx:65"}, "properties": {"repobilityId": "7983a6b0caa0a943", "scanner": "scanner-primary", "fingerprint": "62b51054eaaaabda", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-9113abe2cef93040", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/games/components/DotEditorModal.tsx:48"}, "properties": {"repobilityId": "7638d8a7a0275daf", "scanner": "scanner-primary", "fingerprint": "9113abe2cef93040", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-ba72bb3972e8d2cf", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/key-object/components/KeyObjectTab.tsx:609"}, "properties": {"repobilityId": "0a93bc7d1ddef362", "scanner": "scanner-primary", "fingerprint": "ba72bb3972e8d2cf", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1219a5a7dcf071df", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/key-object/components/KeyObjectDotEditorModal.tsx:39"}, "properties": {"repobilityId": "78164befc2cc9202", "scanner": "scanner-primary", "fingerprint": "1219a5a7dcf071df", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-13050eced504f538", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/tts/components/TtsTab.tsx:112"}, "properties": {"repobilityId": "2510623ba0800787", "scanner": "scanner-primary", "fingerprint": "13050eced504f538", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9669a972802b1ff3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/KoreanPhonicsHeatmap.tsx:191"}, "properties": {"repobilityId": "933a744650888306", "scanner": "scanner-primary", "fingerprint": "9669a972802b1ff3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b5771922c0991f30", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/StorybookReportSection.tsx:63"}, "properties": {"repobilityId": "e045fb869fdaa875", "scanner": "scanner-primary", "fingerprint": "b5771922c0991f30", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-bf5925ec6339d53f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/ArtStyleDistributionCard.tsx:83"}, "properties": {"repobilityId": "3cc665e263d928b8", "scanner": "scanner-primary", "fingerprint": "bf5925ec6339d53f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b9b66f44a73deadd", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/VocabularyMasteryCard.tsx:54"}, "properties": {"repobilityId": "c77f44788a9263c6", "scanner": "scanner-primary", "fingerprint": "b9b66f44a73deadd", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-287eda19138f6e79", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/learning/components/EnglishPhonicsSkillTree.tsx:105"}, "properties": {"repobilityId": "92db7439da657467", "scanner": "scanner-primary", "fingerprint": "287eda19138f6e79", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ec2ac33b9e1fbb15", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/PhonicsViewer.tsx:114"}, "properties": {"repobilityId": "74e4cff1326293dc", "scanner": "scanner-primary", "fingerprint": "ec2ac33b9e1fbb15", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3c7d0bb163737a25", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/ViewerToolbar.tsx:81"}, "properties": {"repobilityId": "110e437ab46358ba", "scanner": "scanner-primary", "fingerprint": "3c7d0bb163737a25", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-aa62310562d4b7be", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/QuizViewer.tsx:71"}, "properties": {"repobilityId": "8fc106f1b7c02e2f", "scanner": "scanner-primary", "fingerprint": "aa62310562d4b7be", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f6c689cfc0c47be2", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:110"}, "properties": {"repobilityId": "5bacd9f7569774c0", "scanner": "scanner-primary", "fingerprint": "f6c689cfc0c47be2", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-88da80d50f3f2824", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/CopyProgressModal.tsx:80"}, "properties": {"repobilityId": "76f22dd0113d1f1c", "scanner": "scanner-primary", "fingerprint": "88da80d50f3f2824", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f70c443003eaf101", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/SidebarCard.tsx:146"}, "properties": {"repobilityId": "fe92adba9d1f2350", "scanner": "scanner-primary", "fingerprint": "f70c443003eaf101", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0ddc44b4c1c379e5", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/Sidebar.tsx:131"}, "properties": {"repobilityId": "6fb9195b5f85251e", "scanner": "scanner-primary", "fingerprint": "0ddc44b4c1c379e5", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7f1a8e5daac55990", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/storybook/components/StorybookCard.tsx:69"}, "properties": {"repobilityId": "31519a006d37fb3a", "scanner": "scanner-primary", "fingerprint": "7f1a8e5daac55990", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-137e335ebae0e8f4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/ProjectSwitcher.tsx:38"}, "properties": {"repobilityId": "c9ed4898e12e349f", "scanner": "scanner-primary", "fingerprint": "137e335ebae0e8f4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-8449cd1b3d715c5d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/layout/SidebarNavItem.tsx:25"}, "properties": {"repobilityId": "695b21c697993863", "scanner": "scanner-primary", "fingerprint": "8449cd1b3d715c5d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c0e95c6064393092", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/PublishQueue.tsx:358"}, "properties": {"repobilityId": "79b4970fde3e0e21", "scanner": "scanner-primary", "fingerprint": "c0e95c6064393092", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0f4e4724924327b7", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/publish/BulkScheduleDialog.tsx:245"}, "properties": {"repobilityId": "2257c3883489a71e", "scanner": "scanner-primary", "fingerprint": "0f4e4724924327b7", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-10760671df4e0d6d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/TopPagesTable.tsx:49"}, "properties": {"repobilityId": "94bf239dcf3affda", "scanner": "scanner-primary", "fingerprint": "10760671df4e0d6d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7ee33ca7e5a581e9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/ContentPerformance.tsx:39"}, "properties": {"repobilityId": "30351c8533c1759d", "scanner": "scanner-primary", "fingerprint": "7ee33ca7e5a581e9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-bedc2671a1f12a6b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:363"}, "properties": {"repobilityId": "082632be02219839", "scanner": "scanner-primary", "fingerprint": "bedc2671a1f12a6b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-77c7bf848cfa84c4", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:437"}, "properties": {"repobilityId": "0cb965fc8eb39be2", "scanner": "scanner-primary", "fingerprint": "77c7bf848cfa84c4", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-ccf3c2861fcc184c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ads/AdsDashboard.tsx:345"}, "properties": {"repobilityId": "b32091b241c3337c", "scanner": "scanner-primary", "fingerprint": "ccf3c2861fcc184c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-07f7b30d6969c06a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/competitors/CompetitorsDashboard.tsx:450"}, "properties": {"repobilityId": "16685697e0cfeeac", "scanner": "scanner-primary", "fingerprint": "07f7b30d6969c06a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4dff0d3f5a7f60b6", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/BlogPreviewDialog.tsx:155"}, "properties": {"repobilityId": "2931778ecb9633cd", "scanner": "scanner-primary", "fingerprint": "4dff0d3f5a7f60b6", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-0f6c50eee7405ab3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubeCardItem.tsx:104"}, "properties": {"repobilityId": "4c678282980004cf", "scanner": "scanner-primary", "fingerprint": "0f6c50eee7405ab3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5fe4259f54487887", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/BaseArticlePanel.tsx:218"}, "properties": {"repobilityId": "1925245f587ce5a0", "scanner": "scanner-primary", "fingerprint": "5fe4259f54487887", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5b95fba94cb9638c", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/content/ChannelTranslationView.tsx:43"}, "properties": {"repobilityId": "0adcf00725999ca3", "scanner": "scanner-primary", "fingerprint": "5b95fba94cb9638c", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-30cdcecb4206d6fd", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/CardNewsPanel.tsx:177"}, "properties": {"repobilityId": "5e3584241d63acd1", "scanner": "scanner-primary", "fingerprint": "30cdcecb4206d6fd", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-03b7b1046c099e20", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/content/YoutubePanel.tsx:434"}, "properties": {"repobilityId": "adb2c703f6baae73", "scanner": "scanner-primary", "fingerprint": "03b7b1046c099e20", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e65cfe4ff025574f", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx:117"}, "properties": {"repobilityId": "2a314d25db70b5d3", "scanner": "scanner-primary", "fingerprint": "e65cfe4ff025574f", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-0c92f75325a4947d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/ideas/TrendingFeed.tsx:68"}, "properties": {"repobilityId": "92b71c894ba20dbf", "scanner": "scanner-primary", "fingerprint": "0c92f75325a4947d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c7331e4f5384935c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/BgmSection.tsx:177"}, "properties": {"repobilityId": "286548e86020cabe", "scanner": "scanner-primary", "fingerprint": "c7331e4f5384935c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ae96ae190bb69048", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/WritingGuideSection.tsx:204"}, "properties": {"repobilityId": "aa3f3602a75bfb8f", "scanner": "scanner-primary", "fingerprint": "ae96ae190bb69048", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5b9aada34a51592e", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:273"}, "properties": {"repobilityId": "52e90328dbdee14a", "scanner": "scanner-primary", "fingerprint": "5b9aada34a51592e", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e3151580adfb9621", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/LongformProjectHeader.tsx:23"}, "properties": {"repobilityId": "c227acc84b2b3de9", "scanner": "scanner-primary", "fingerprint": "e3151580adfb9621", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-89a79761fd289f92", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/TimelineEditorStep.tsx:628"}, "properties": {"repobilityId": "3b263a38bf753851", "scanner": "scanner-primary", "fingerprint": "89a79761fd289f92", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1d4659a9a90947c8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/longform-video/components/PromptPresetModal.tsx:174"}, "properties": {"repobilityId": "2bcce3e39e4670ed", "scanner": "scanner-primary", "fingerprint": "1d4659a9a90947c8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ca60369d20e32df0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyStudyPage.tsx:117"}, "properties": {"repobilityId": "bc109fb865dc36fc", "scanner": "scanner-primary", "fingerprint": "ca60369d20e32df0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7627f602dc0e1316", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyUnitSidebarList.tsx:130"}, "properties": {"repobilityId": "f5afacffc94dabf9", "scanner": "scanner-primary", "fingerprint": "7627f602dc0e1316", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-2962dcd3fe50fd50", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary-unit/components/VocabularyStudyContent.tsx:204"}, "properties": {"repobilityId": "1ce285b2d1804769", "scanner": "scanner-primary", "fingerprint": "2962dcd3fe50fd50", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-781481823e86c9de", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/vocabulary/components/VocabTreeModal.tsx:265"}, "properties": {"repobilityId": "2a3902eca33b5c0d", "scanner": "scanner-primary", "fingerprint": "781481823e86c9de", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f2d02ae53756b2f9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/LevelEditCard.tsx:167"}, "properties": {"repobilityId": "93dd30f1133ea8a8", "scanner": "scanner-primary", "fingerprint": "f2d02ae53756b2f9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-cd2b9e59a37ba754", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/OtherStyleReference.tsx:93"}, "properties": {"repobilityId": "6f524b692ead8ddf", "scanner": "scanner-primary", "fingerprint": "cd2b9e59a37ba754", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a0ff377425574604", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/BookManageTab.tsx:62"}, "properties": {"repobilityId": "c4fe0851005f32de", "scanner": "scanner-primary", "fingerprint": "a0ff377425574604", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-dcfe885a8c6387ad", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/editor/components/EditorPanelV2.tsx:157"}, "properties": {"repobilityId": "0d5e1f87f709fd2f", "scanner": "scanner-primary", "fingerprint": "dcfe885a8c6387ad", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-554925ff707d6426", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterTab.tsx:343"}, "properties": {"repobilityId": "b5b2d69e1b8a5456", "scanner": "scanner-primary", "fingerprint": "554925ff707d6426", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-78c9c35d13e151ec", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/character/components/CharacterLibraryModal.tsx:345"}, "properties": {"repobilityId": "cd8d7bf802f172a4", "scanner": "scanner-primary", "fingerprint": "78c9c35d13e151ec", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3b62098860021068", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/phonics/components/TtsRow.tsx:47"}, "properties": {"repobilityId": "e1c4b29319a1890d", "scanner": "scanner-primary", "fingerprint": "3b62098860021068", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-732552c441693bae", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/phonics/components/HotspotEditorModal.tsx:317"}, "properties": {"repobilityId": "7a2985d2abeedec2", "scanner": "scanner-primary", "fingerprint": "732552c441693bae", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-1b5e8c71241e52a9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/audiobook/components/AudiobookProjectCard.tsx:655"}, "properties": {"repobilityId": "ba611c31acf5e463", "scanner": "scanner-primary", "fingerprint": "1b5e8c71241e52a9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-50968962711495d9", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/YouTubeUploadModal.tsx:179"}, "properties": {"repobilityId": "95b1cfae8dbb7ef2", "scanner": "scanner-primary", "fingerprint": "50968962711495d9", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-de31b1d5de9b57c0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/GamesTab.tsx:174"}, "properties": {"repobilityId": "9a247866622d6d42", "scanner": "scanner-primary", "fingerprint": "de31b1d5de9b57c0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-105f47d40c84b422", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/AudiobookRenderModal.tsx:101"}, "properties": {"repobilityId": "da3d760e71671806", "scanner": "scanner-primary", "fingerprint": "105f47d40c84b422", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-c150f364964c2410", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CaptionsModal.tsx:142"}, "properties": {"repobilityId": "595dfe9053836773", "scanner": "scanner-primary", "fingerprint": "c150f364964c2410", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-2a8a2b9ee92b18f4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/MarketingTab.tsx:134"}, "properties": {"repobilityId": "488837146e5a481d", "scanner": "scanner-primary", "fingerprint": "2a8a2b9ee92b18f4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9af4cc53925fee39", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/TextTab.tsx:225"}, "properties": {"repobilityId": "f880ce239638371d", "scanner": "scanner-primary", "fingerprint": "9af4cc53925fee39", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ff0e1e2619d5800d", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx:253"}, "properties": {"repobilityId": "3390437f38effc4f", "scanner": "scanner-primary", "fingerprint": "ff0e1e2619d5800d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-cf68c940362aaf70", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/BookListSidebar.tsx:205"}, "properties": {"repobilityId": "bcbcee88ce484dc0", "scanner": "scanner-primary", "fingerprint": "cf68c940362aaf70", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c48c0f4b0d8aa5ad", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/LongformTab.tsx:328"}, "properties": {"repobilityId": "4284bf2afdf99d34", "scanner": "scanner-primary", "fingerprint": "c48c0f4b0d8aa5ad", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9f920a6ca17f28b4", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/CreateLongformModal.tsx:52"}, "properties": {"repobilityId": "d2de25e88a7191c5", "scanner": "scanner-primary", "fingerprint": "9f920a6ca17f28b4", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-091af1e235ea215a", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/book-v2/components/SceneEditor.tsx:65"}, "properties": {"repobilityId": "3a8a59a8ef43237d", "scanner": "scanner-primary", "fingerprint": "091af1e235ea215a", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-7da8bb8912d6b036", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/book-v2/components/AudiobookTab.tsx:232"}, "properties": {"repobilityId": "27f4a6adef6e40df", "scanner": "scanner-primary", "fingerprint": "7da8bb8912d6b036", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ba2d2e990b445d70", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsPreviewModal.tsx:158"}, "properties": {"repobilityId": "78cdc2897d30566c", "scanner": "scanner-primary", "fingerprint": "ba2d2e990b445d70", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-3886d48b22593afb", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsConfigForm.tsx:126"}, "properties": {"repobilityId": "5e16e63d8c0c1a61", "scanner": "scanner-primary", "fingerprint": "3886d48b22593afb", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-80b82ae6a40592fe", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsProjectCard.tsx:97"}, "properties": {"repobilityId": "4c4955568ce6503e", "scanner": "scanner-primary", "fingerprint": "80b82ae6a40592fe", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7031ab52b0c8b255", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 packages/client/src/features/card-news/components/CardNewsSlideEditor.tsx:75"}, "properties": {"repobilityId": "0beba9e83320e350", "scanner": "scanner-primary", "fingerprint": "7031ab52b0c8b255", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-1b03c242cc83b8e4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 packages/client/src/features/card-news/components/CardNewsSlideEditor.tsx:160"}, "properties": {"repobilityId": "c17c77a196c6fa28", "scanner": "scanner-primary", "fingerprint": "1b03c242cc83b8e4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-370f10af52ade80b", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/restore-from-manifest.ts:12"}, "properties": {"repobilityId": "cad85bed454e4e68", "scanner": "scanner-primary", "fingerprint": "370f10af52ade80b", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-854f65852f63c5dd", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/check-storybook-urls.ts:28"}, "properties": {"repobilityId": "2c60249dd88a721f", "scanner": "scanner-primary", "fingerprint": "854f65852f63c5dd", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-abd73e6010ae6eb2", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/find-storybook.ts:11"}, "properties": {"repobilityId": "f52feeebc7cd2bfd", "scanner": "scanner-primary", "fingerprint": "abd73e6010ae6eb2", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-1b3303b4df3197e0", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/migrate-assets.ts:33"}, "properties": {"repobilityId": "f2e03d844bd3a775", "scanner": "scanner-primary", "fingerprint": "1b3303b4df3197e0", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-32bc89c55ed2fa69", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/count-manifests.ts:6"}, "properties": {"repobilityId": "9ddcd2795a3a9963", "scanner": "scanner-primary", "fingerprint": "32bc89c55ed2fa69", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-b50bd503ad4a5f1c", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-url-walker.ts:7"}, "properties": {"repobilityId": "0842f478fd422880", "scanner": "scanner-primary", "fingerprint": "b50bd503ad4a5f1c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-cc3027b740f2149c", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/scripts/verify-transcode.ts:15"}, "properties": {"repobilityId": "033de427eb96855e", "scanner": "scanner-primary", "fingerprint": "cc3027b740f2149c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-d9260469ac09ed9c", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/utils/remotion-bundle.ts:39"}, "properties": {"repobilityId": "e4266b81abdc772e", "scanner": "scanner-primary", "fingerprint": "d9260469ac09ed9c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-6436dee63eb03109", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/grok.provider.ts:58"}, "properties": {"repobilityId": "da960fd68a19040a", "scanner": "scanner-primary", "fingerprint": "6436dee63eb03109", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-751694188133583a", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/youtube.provider.ts:120"}, "properties": {"repobilityId": "ac85e1cb3fe4cc84", "scanner": "scanner-primary", "fingerprint": "751694188133583a", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-d1e41df8cc64df6a", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/providers/longform.provider.ts:121"}, "properties": {"repobilityId": "b7893fa1c784d0b0", "scanner": "scanner-primary", "fingerprint": "d1e41df8cc64df6a", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-77e4b053cd3f0a03", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/audiobook.service.ts:70"}, "properties": {"repobilityId": "ec181cecf45bf1d3", "scanner": "scanner-primary", "fingerprint": "77e4b053cd3f0a03", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-153c56276a7b3c40", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/storybook.service.ts:206"}, "properties": {"repobilityId": "f091d09eb64afe42", "scanner": "scanner-primary", "fingerprint": "153c56276a7b3c40", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-0a0b4a8a989d3a92", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/longform.service.ts:717"}, "properties": {"repobilityId": "1e106e85d74bdae9", "scanner": "scanner-primary", "fingerprint": "0a0b4a8a989d3a92", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-b7c3b7670d83ee0d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 packages/server/src/services/mkt/external/__tests__/naver-searchad.test.ts:13"}, "properties": {"repobilityId": "4147fabe98b1efd0", "scanner": "scanner-primary", "fingerprint": "b7c3b7670d83ee0d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-d63da3583b14afc0", "level": "warning", "message": {"text": "Dockerfile runs as root: Dockerfile"}, "properties": {"repobilityId": "a2ed1bd120e507db", "scanner": "scanner-primary", "fingerprint": "d63da3583b14afc0", "layer": "hardware", "severity": "medium", "confidence": 1.0, "tags": ["security", "container"]}}, {"ruleId": "scanner-8987d6d1c30c7202", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:20-alpine"}, "properties": {"repobilityId": "068fbf45727eac17", "scanner": "scanner-primary", "fingerprint": "8987d6d1c30c7202", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-aa5acaa49eb8315b", "level": "note", "message": {"text": "Containers defined but no K8s/orchestration manifest found"}, "properties": {"repobilityId": "b230ea9b68736081", "scanner": "scanner-primary", "fingerprint": "aa5acaa49eb8315b", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["coverage", "deployment"]}}, {"ruleId": "scanner-0befc492f97c4f13", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master.html:403"}, "properties": {"repobilityId": "4660e77bdd626473", "scanner": "scanner-primary", "fingerprint": "0befc492f97c4f13", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docs/books/vocabulary-master.html"}, "region": {"startLine": 403}}}]}, {"ruleId": "scanner-21c82ef57db50efb", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/curriculum-master.html:457"}, "properties": {"repobilityId": "b777050a55f6e39b", "scanner": "scanner-primary", "fingerprint": "21c82ef57db50efb", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docs/books/curriculum-master.html"}, "region": {"startLine": 457}}}]}, {"ruleId": "scanner-b460a7e75f82c5c9", "level": "warning", "message": {"text": "Insecure pattern 'insert_adjacent_html' in docs/books/curriculum-master.html:500"}, "properties": {"repobilityId": "edc924fabcb00e41", "scanner": "scanner-primary", "fingerprint": "b460a7e75f82c5c9", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "insert_adjacent_html"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docs/books/curriculum-master.html"}, "region": {"startLine": 500}}}]}, {"ruleId": "scanner-05a6acfc4a9cca39", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/books/vocabulary-master-ko.html:388"}, "properties": {"repobilityId": "5ecaf2d2f72461cb", "scanner": "scanner-primary", "fingerprint": "05a6acfc4a9cca39", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docs/books/vocabulary-master-ko.html"}, "region": {"startLine": 388}}}]}, {"ruleId": "scanner-f7b356fd0ce5a138", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in scripts/synthesize-game-sfx.mjs:9"}, "properties": {"repobilityId": "1afbe19414a2a943", "scanner": "scanner-primary", "fingerprint": "f7b356fd0ce5a138", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "scripts/synthesize-game-sfx.mjs"}, "region": {"startLine": 9}}}]}, {"ruleId": "scanner-f549030b17038831", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in scripts/synthesize-runner-sfx.mjs:7"}, "properties": {"repobilityId": "0b9c20d102ead0d3", "scanner": "scanner-primary", "fingerprint": "f549030b17038831", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "scripts/synthesize-runner-sfx.mjs"}, "region": {"startLine": 7}}}]}, {"ruleId": "scanner-2c35cf40fd528d68", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/design-system.html:566"}, "properties": {"repobilityId": "95a605ed8bc26903", "scanner": "scanner-primary", "fingerprint": "2c35cf40fd528d68", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/design-system.html"}, "region": {"startLine": 566}}}]}, {"ruleId": "scanner-a5bb615d8263d61d", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master.html:426"}, "properties": {"repobilityId": "d1f875bccd53aa44", "scanner": "scanner-primary", "fingerprint": "a5bb615d8263d61d", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/vocabulary-master.html"}, "region": {"startLine": 426}}}]}, {"ruleId": "scanner-808d7ecabebbe0fb", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/curriculum-master.html:457"}, "properties": {"repobilityId": "f12c3e8af07b9ba0", "scanner": "scanner-primary", "fingerprint": "808d7ecabebbe0fb", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/curriculum-master.html"}, "region": {"startLine": 457}}}]}, {"ruleId": "scanner-b7a73b7b777cebc9", "level": "warning", "message": {"text": "Insecure pattern 'insert_adjacent_html' in packages/client/public/curriculum-master.html:500"}, "properties": {"repobilityId": "a20a46ac7bc916c3", "scanner": "scanner-primary", "fingerprint": "b7a73b7b777cebc9", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "insert_adjacent_html"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/curriculum-master.html"}, "region": {"startLine": 500}}}]}, {"ruleId": "scanner-fad127e6c8426587", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-master-ko.html:388"}, "properties": {"repobilityId": "a1bb063c0f3423ea", "scanner": "scanner-primary", "fingerprint": "fad127e6c8426587", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/vocabulary-master-ko.html"}, "region": {"startLine": 388}}}]}, {"ruleId": "scanner-475289d0b31e6ffc", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/vocabulary-table-ko.html:397"}, "properties": {"repobilityId": "9254a0d7e0a640cb", "scanner": "scanner-primary", "fingerprint": "475289d0b31e6ffc", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/vocabulary-table-ko.html"}, "region": {"startLine": 397}}}]}, {"ruleId": "scanner-18af250cd451a055", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/public/key-object-editor.html:232"}, "properties": {"repobilityId": "63ce725ead530495", "scanner": "scanner-primary", "fingerprint": "18af250cd451a055", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/public/key-object-editor.html"}, "region": {"startLine": 232}}}]}, {"ruleId": "scanner-abde877323fc5e7c", "level": "error", "message": {"text": "Insecure pattern 'exec_used' in packages/client/src/components/RichTextEditor.tsx:134"}, "properties": {"repobilityId": "6fce9a38ade0faa8", "scanner": "scanner-primary", "fingerprint": "abde877323fc5e7c", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["owasp", "exec_used"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/components/RichTextEditor.tsx"}, "region": {"startLine": 134}}}]}, {"ruleId": "scanner-0c3ca11419710b4c", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/components/RichTextEditor.tsx:106"}, "properties": {"repobilityId": "e53255ffa7131332", "scanner": "scanner-primary", "fingerprint": "0c3ca11419710b4c", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/components/RichTextEditor.tsx"}, "region": {"startLine": 106}}}]}, {"ruleId": "scanner-e5d7c4a62bbb5164", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:99"}, "properties": {"repobilityId": "fd3e3a3dcfca92c3", "scanner": "scanner-primary", "fingerprint": "e5d7c4a62bbb5164", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/blog/components/BlogPreviewModal.tsx"}, "region": {"startLine": 99}}}]}, {"ruleId": "scanner-44e4942c454fb259", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/blog/components/BlogPreviewModal.tsx:58"}, "properties": {"repobilityId": "e2c96d64be3ee633", "scanner": "scanner-primary", "fingerprint": "44e4942c454fb259", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/blog/components/BlogPreviewModal.tsx"}, "region": {"startLine": 58}}}]}, {"ruleId": "scanner-1e2ff91d9336f4ac", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/games/components/players/EnglishBlockPlayer.tsx:52"}, "properties": {"repobilityId": "b137b5dbb0252d2f", "scanner": "scanner-primary", "fingerprint": "1e2ff91d9336f4ac", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/games/components/players/EnglishBlockPlayer.tsx"}, "region": {"startLine": 52}}}]}, {"ruleId": "scanner-f506915d452517f7", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:437"}, "properties": {"repobilityId": "1f5d4d927e16b2d5", "scanner": "scanner-primary", "fingerprint": "f506915d452517f7", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx"}, "region": {"startLine": 437}}}]}, {"ruleId": "scanner-f99c3326a1cb1577", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/BlogPreviewDialog.tsx:155"}, "properties": {"repobilityId": "3f6e4820a41a4b0e", "scanner": "scanner-primary", "fingerprint": "f99c3326a1cb1577", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/content/BlogPreviewDialog.tsx"}, "region": {"startLine": 155}}}]}, {"ruleId": "scanner-4e21c8cd9b2a77ea", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/BlogCardItem.tsx:23"}, "properties": {"repobilityId": "c59e42e60227d2d7", "scanner": "scanner-primary", "fingerprint": "4e21c8cd9b2a77ea", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/content/BlogCardItem.tsx"}, "region": {"startLine": 23}}}]}, {"ruleId": "scanner-39295dc2ede41f35", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/content/ChannelTranslationView.tsx:43"}, "properties": {"repobilityId": "f214842ce2febd9f", "scanner": "scanner-primary", "fingerprint": "39295dc2ede41f35", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/content/ChannelTranslationView.tsx"}, "region": {"startLine": 43}}}]}, {"ruleId": "scanner-cd8754d4b13449f1", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in packages/client/src/features/marketing/components/content/__tests__/BlogCardItem.formatForMobile.test.tsx:10"}, "properties": {"repobilityId": "5df083b266e758bb", "scanner": "scanner-primary", "fingerprint": "cd8754d4b13449f1", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/content/__tests__/BlogCardItem.formatForMobile.test.tsx"}, "region": {"startLine": 10}}}]}, {"ruleId": "scanner-655fb1745c2ffe3a", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx:117"}, "properties": {"repobilityId": "7f0fd998094b4bf1", "scanner": "scanner-primary", "fingerprint": "655fb1745c2ffe3a", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/ideas/GoldenTierCards.tsx"}, "region": {"startLine": 117}}}]}, {"ruleId": "scanner-96e34e0bad9599ce", "level": "error", "message": {"text": "Insecure pattern 'private_key_in_repo' in packages/client/src/features/marketing/components/project/sections/FunnelAnalyticsSection.tsx:182"}, "properties": {"repobilityId": "3e42ad0562350a37", "scanner": "scanner-primary", "fingerprint": "96e34e0bad9599ce", "layer": "security", "severity": "critical", "confidence": 1.0, "tags": ["owasp", "private_key_in_repo"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/components/project/sections/FunnelAnalyticsSection.tsx"}, "region": {"startLine": 182}}}]}, {"ruleId": "scanner-7772cebe56f38d7d", "level": "warning", "message": {"text": "Insecure pattern 'domparser_html_parse' in packages/client/src/features/marketing/lib/strategy-html-parser.ts:93"}, "properties": {"repobilityId": "e5e5e6d207041279", "scanner": "scanner-primary", "fingerprint": "7772cebe56f38d7d", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "domparser_html_parse"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/lib/strategy-html-parser.ts"}, "region": {"startLine": 93}}}]}, {"ruleId": "scanner-00282ebc72334c7d", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:26"}, "properties": {"repobilityId": "37e22d419c11a14f", "scanner": "scanner-primary", "fingerprint": "00282ebc72334c7d", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs"}, "region": {"startLine": 26}}}]}, {"ruleId": "scanner-337f03dc810f252a", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/strip-checkerboard-bg.mjs:6"}, "properties": {"repobilityId": "922ddfa6a02f9624", "scanner": "scanner-primary", "fingerprint": "337f03dc810f252a", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/strip-checkerboard-bg.mjs"}, "region": {"startLine": 6}}}]}, {"ruleId": "scanner-2f28dfbcbb8aa015", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/convert-png-to-webp.mjs:10"}, "properties": {"repobilityId": "3ba55231a03c1394", "scanner": "scanner-primary", "fingerprint": "2f28dfbcbb8aa015", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/convert-png-to-webp.mjs"}, "region": {"startLine": 10}}}]}, {"ruleId": "scanner-e6cd86a0374366f9", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/normalize-bgm-volume.mjs:18"}, "properties": {"repobilityId": "db7a4a44c1e43e82", "scanner": "scanner-primary", "fingerprint": "e6cd86a0374366f9", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/normalize-bgm-volume.mjs"}, "region": {"startLine": 18}}}]}, {"ruleId": "scanner-14203f21f6f5a043", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/scripts/merge-and-apply-batch.mjs:6"}, "properties": {"repobilityId": "247afb406bbc6451", "scanner": "scanner-primary", "fingerprint": "14203f21f6f5a043", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/scripts/merge-and-apply-batch.mjs"}, "region": {"startLine": 6}}}]}, {"ruleId": "scanner-bfd123e4f862656f", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/utils/transcode.ts:2"}, "properties": {"repobilityId": "eb48814cafa602e1", "scanner": "scanner-primary", "fingerprint": "bfd123e4f862656f", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/utils/transcode.ts"}, "region": {"startLine": 2}}}]}, {"ruleId": "scanner-d9be0d62416002f6", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/utils/audio-duration.ts:5"}, "properties": {"repobilityId": "c4772ab53018f8a9", "scanner": "scanner-primary", "fingerprint": "d9be0d62416002f6", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/utils/audio-duration.ts"}, "region": {"startLine": 5}}}]}, {"ruleId": "scanner-b12d43d3fe1edb8e", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/providers/longform.provider.ts:1"}, "properties": {"repobilityId": "a400d4ff8d955aea", "scanner": "scanner-primary", "fingerprint": "b12d43d3fe1edb8e", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/providers/longform.provider.ts"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-020aad46d9d6e153", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/book-v2.service.ts:67"}, "properties": {"repobilityId": "fcb875386108be58", "scanner": "scanner-primary", "fingerprint": "020aad46d9d6e153", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/services/book-v2.service.ts"}, "region": {"startLine": 67}}}]}, {"ruleId": "scanner-11c7cabad7383f97", "level": "error", "message": {"text": "Insecure pattern 'new_function_used' in packages/server/src/services/book-v2-curriculum-seed.service.ts:71"}, "properties": {"repobilityId": "8222430e618e1bec", "scanner": "scanner-primary", "fingerprint": "11c7cabad7383f97", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["owasp", "new_function_used"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/services/book-v2-curriculum-seed.service.ts"}, "region": {"startLine": 71}}}]}, {"ruleId": "scanner-70e4fb685e388105", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/phonics-library.service.ts:14"}, "properties": {"repobilityId": "23c859e4165f5f91", "scanner": "scanner-primary", "fingerprint": "70e4fb685e388105", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/services/phonics-library.service.ts"}, "region": {"startLine": 14}}}]}, {"ruleId": "scanner-106f09b8940e6b76", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in packages/server/src/services/longform.service.ts:4"}, "properties": {"repobilityId": "787d1053b7b977d9", "scanner": "scanner-primary", "fingerprint": "106f09b8940e6b76", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/src/services/longform.service.ts"}, "region": {"startLine": 4}}}]}, {"ruleId": "scanner-abca4ea64c23d07a", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in supabase/functions/reset-pin/index.ts:15"}, "properties": {"repobilityId": "3021dd8ffc540b73", "scanner": "scanner-primary", "fingerprint": "abca4ea64c23d07a", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "supabase/functions/reset-pin/index.ts"}, "region": {"startLine": 15}}}]}, {"ruleId": "scanner-cb639d9a10a47b23", "level": "note", "message": {"text": "package.json defines install-time lifecycle scripts"}, "properties": {"repobilityId": "b86179956da3a4a8", "scanner": "scanner-primary", "fingerprint": "cb639d9a10a47b23", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "npm", "install-scripts"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "package.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-4601e3ad3bb28677", "level": "warning", "message": {"text": "No CI/CD pipelines detected"}, "properties": {"repobilityId": "c3ee439bce2bc51e", "scanner": "scanner-primary", "fingerprint": "4601e3ad3bb28677", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-89bd408acbba45d5", "level": "note", "message": {"text": "Very large file: packages/client/src/features/arcade-games/hori-run/scenes/GameScene.ts (1717 lines)"}, "properties": {"repobilityId": "f9287b6274363dd8", "scanner": "scanner-primary", "fingerprint": "89bd408acbba45d5", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-271bd2e0da0373d7", "level": "note", "message": {"text": "Very large file: packages/client/src/features/marketing/components/content/CardNewsPanel.tsx (1285 lines)"}, "properties": {"repobilityId": "2063a77e1c07dc77", "scanner": "scanner-primary", "fingerprint": "271bd2e0da0373d7", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-8b6ae7fe2600365b", "level": "note", "message": {"text": "Very large file: packages/client/src/features/longform-video/components/RenderStep.tsx (1574 lines)"}, "properties": {"repobilityId": "c826da40ca3e05d2", "scanner": "scanner-primary", "fingerprint": "8b6ae7fe2600365b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-a3d7f5ccd1e97e0d", "level": "note", "message": {"text": "Very large file: packages/client/src/features/audiobook/components/AudiobookProjectCard.tsx (1896 lines)"}, "properties": {"repobilityId": "52370918e6c27338", "scanner": "scanner-primary", "fingerprint": "a3d7f5ccd1e97e0d", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-1a48c513401e6a03", "level": "note", "message": {"text": "Very large file: packages/shared/src/constants/index.ts (1737 lines)"}, "properties": {"repobilityId": "7f0ccf9f7420d628", "scanner": "scanner-primary", "fingerprint": "1a48c513401e6a03", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-fa31210f41dc116e", "level": "note", "message": {"text": "Very large file: packages/server/src/services/book-v2.service.ts (2065 lines)"}, "properties": {"repobilityId": "6766b8d5f085e2de", "scanner": "scanner-primary", "fingerprint": "fa31210f41dc116e", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-80a6bc9f852a87e7", "level": "note", "message": {"text": "Very large file: packages/server/src/services/longform.service.ts (1717 lines)"}, "properties": {"repobilityId": "965a653cad2596fb", "scanner": "scanner-primary", "fingerprint": "80a6bc9f852a87e7", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-ea3b5e389d8c9c0f", "level": "note", "message": {"text": "Low test-to-source ratio"}, "properties": {"repobilityId": "ef7b2552cc00a375", "scanner": "scanner-primary", "fingerprint": "ea3b5e389d8c9c0f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["tests"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "c4f9a661bf75b5cb", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-01f96f71d9907a8c", "level": "note", "message": {"text": "README lacks setup or run instructions"}, "properties": {"repobilityId": "113149cfb0420b77", "scanner": "scanner-primary", "fingerprint": "01f96f71d9907a8c", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["docs", "readme", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-f1f0c52db7c97cef", "level": "note", "message": {"text": "Node manifest has dependencies but no lockfile: packages/client/package.json"}, "properties": {"repobilityId": "708197ec3b624f59", "scanner": "scanner-primary", "fingerprint": "f1f0c52db7c97cef", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["dependency", "lockfile", "reproducibility", "generated-repo-pattern"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/package.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-2699d5f16ae11282", "level": "note", "message": {"text": "Node manifest has dependencies but no lockfile: packages/shared/package.json"}, "properties": {"repobilityId": "37ac218a4ef9b430", "scanner": "scanner-primary", "fingerprint": "2699d5f16ae11282", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["dependency", "lockfile", "reproducibility", "generated-repo-pattern"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/shared/package.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-dd7164eb7a3ac055", "level": "note", "message": {"text": "Node manifest has dependencies but no lockfile: packages/remotion/package.json"}, "properties": {"repobilityId": "80c45d3b50def293", "scanner": "scanner-primary", "fingerprint": "dd7164eb7a3ac055", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["dependency", "lockfile", "reproducibility", "generated-repo-pattern"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/remotion/package.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-b739f2f2be1d714d", "level": "note", "message": {"text": "Node manifest has dependencies but no lockfile: packages/server/package.json"}, "properties": {"repobilityId": "d7cb4fafc3b292b2", "scanner": "scanner-primary", "fingerprint": "b739f2f2be1d714d", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["dependency", "lockfile", "reproducibility", "generated-repo-pattern"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/server/package.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "d7b62bcbf24581b2", "scanner": "scanner-primary", "fingerprint": "3ab5d313dda8e5f9", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["debug", "cleanup", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-72b2a6250083a784", "level": "warning", "message": {"text": "Placeholder or mock-heavy implementation detected"}, "properties": {"repobilityId": "4ce8c3e8826d39dd", "scanner": "scanner-primary", "fingerprint": "72b2a6250083a784", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "incomplete", "generated-repo-pattern"]}}, {"ruleId": "scanner-9d79c4077342a7d0", "level": "warning", "message": {"text": "Runtime service client appears to use placeholder configuration"}, "properties": {"repobilityId": "f4d41a4a7509f4a5", "scanner": "scanner-primary", "fingerprint": "9d79c4077342a7d0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "runtime-config", "service-client", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "4a4eba575a42dd3b", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-b9088664ace7f748", "level": "warning", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "3acb8cee01455b6b", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["production-readiness", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-749d4bc1bd66df5f", "level": "warning", "message": {"text": "Agent instructions exist but release-hardening basics are missing"}, "properties": {"repobilityId": "5261a14db7495b55", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "07afb6b50e2c68e6", "scanner": "scanner-primary", "fingerprint": "ea8f3013f588db25", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-8424db9c75e04ba4", "level": "none", "message": {"text": "Very short observed git history"}, "properties": {"repobilityId": "33ad7a603b2e11cd", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-5ae453a25c37091f", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/arcade-games/CLAUDE.md"}, "properties": {"repobilityId": "4b89384b99c5395c", "scanner": "scanner-primary", "fingerprint": "5ae453a25c37091f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/arcade-games/CLAUDE.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d8282f1d7e8b64b9", "level": "error", "message": {"text": "Agent instruction/config may expose a secret: packages/client/src/features/marketing/CLAUDE.md"}, "properties": {"repobilityId": "66ba93ac7c79389e", "scanner": "scanner-primary", "fingerprint": "d8282f1d7e8b64b9", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["agent-instructions", "secrets", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/CLAUDE.md"}, "region": {"startLine": 293}}}]}, {"ruleId": "scanner-f2a28dec14a4613e", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/marketing/CLAUDE.md"}, "properties": {"repobilityId": "4e4f16b52b107d0d", "scanner": "scanner-primary", "fingerprint": "f2a28dec14a4613e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/marketing/CLAUDE.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-172ae6d897eed928", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/longform-video/CLAUDE.md"}, "properties": {"repobilityId": "c4a2e5b7705f9247", "scanner": "scanner-primary", "fingerprint": "172ae6d897eed928", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/longform-video/CLAUDE.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-24c71aec70549a7c", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: packages/client/src/features/rewards/CLAUDE.md"}, "properties": {"repobilityId": "80d445a736017ace", "scanner": "scanner-primary", "fingerprint": "24c71aec70549a7c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "packages/client/src/features/rewards/CLAUDE.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-f0fa2986a2d92926", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-keyword-research.mjs:49"}, "properties": {"repobilityId": "0ad4d464c14e6f2d", "scanner": "scanner-primary", "fingerprint": "f0fa2986a2d92926", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-b92db8caef046e08", "level": "none", "message": {"text": "Commented-code block (8 lines) in docs/marketing/scripts/audit-noise-and-mine.mjs:17"}, "properties": {"repobilityId": "674c3af2cdf80b9d", "scanner": "scanner-primary", "fingerprint": "b92db8caef046e08", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f6bf536832fdb6d8", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/dataforseo-keyword-research.mjs:186"}, "properties": {"repobilityId": "fe8e8eb1944e4afc", "scanner": "scanner-primary", "fingerprint": "f6bf536832fdb6d8", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-8048a0cf59e4dc05", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 docs/marketing/scripts/naver-content-keyword-research.mjs:58"}, "properties": {"repobilityId": "8468e59f49b1fc9b", "scanner": "scanner-primary", "fingerprint": "8048a0cf59e4dc05", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-683bd04d1e15b6fa", "level": "none", "message": {"text": "Commented-code block (9 lines) in docs/marketing/scripts/consolidate-keywords.mjs:2"}, "properties": {"repobilityId": "49305ddfbc8126e6", "scanner": "scanner-primary", "fingerprint": "683bd04d1e15b6fa", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-6a91c5948e1ff27e", "level": "none", "message": {"text": "Commented-code block (5 lines) in scripts/generate-pixar-3d-jack.mjs:1"}, "properties": {"repobilityId": "33b994ce0a6a5303", "scanner": "scanner-primary", "fingerprint": "6a91c5948e1ff27e", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-3af3fde10e323988", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/generate-pixar-3d-jack.mjs:15"}, "properties": {"repobilityId": "66284c9643fd4cf1", "scanner": "scanner-primary", "fingerprint": "3af3fde10e323988", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ed9c5df4add4f7f0", "level": "none", "message": {"text": "Commented-code block (5 lines) in scripts/bulk-setup-complete-books.mjs:1"}, "properties": {"repobilityId": "d0f31d148dac4e53", "scanner": "scanner-primary", "fingerprint": "ed9c5df4add4f7f0", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-e03f14eba8ab095d", "level": "none", "message": {"text": "Commented-code block (10 lines) in scripts/audit-v1-storybooks.mjs:3"}, "properties": {"repobilityId": "6d2d61611dfa728d", "scanner": "scanner-primary", "fingerprint": "e03f14eba8ab095d", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-2821ed6d8d83e77c", "level": "none", "message": {"text": "Commented-code block (7 lines) in scripts/seed-phonics-books.mjs:12"}, "properties": {"repobilityId": "7e65448bdd40318c", "scanner": "scanner-primary", "fingerprint": "2821ed6d8d83e77c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-94b552b51415c6ad", "level": "none", "message": {"text": "Commented-code block (14 lines) in scripts/migrate-vocabulary-to-keyobjects.mjs:3"}, "properties": {"repobilityId": "d810990a862b62e3", "scanner": "scanner-primary", "fingerprint": "94b552b51415c6ad", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f0e0a9e0b4b61184", "level": "none", "message": {"text": "Commented-code block (16 lines) in scripts/fill-reading-level.mjs:3"}, "properties": {"repobilityId": "eba7175924a7baf1", "scanner": "scanner-primary", "fingerprint": "f0e0a9e0b4b61184", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c1bd985450c734bf", "level": "none", "message": {"text": "Commented-code block (5 lines) in scripts/migrate-korean-phonics-words.mjs:7"}, "properties": {"repobilityId": "1633a0804160200b", "scanner": "scanner-primary", "fingerprint": "c1bd985450c734bf", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-3aca6a4fd5a63c44", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 scripts/seed-hori-stub.mjs:40"}, "properties": {"repobilityId": "cc3fcee73a2e56ee", "scanner": "scanner-primary", "fingerprint": "3aca6a4fd5a63c44", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e73d8f51c1e6c05e", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272"}, "properties": {"repobilityId": "fdad673be3fa97fe", "scanner": "scanner-primary", "fingerprint": "e73d8f51c1e6c05e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a90c554873f123a7", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/client/src/pages/BookDetailPage.tsx:169"}, "properties": {"repobilityId": "13b8df16e8eff317", "scanner": "scanner-primary", "fingerprint": "a90c554873f123a7", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-fde5988f1ed7583b", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/hooks/useAssetPreloadProgress.ts:18"}, "properties": {"repobilityId": "e109ce9667c7b484", "scanner": "scanner-primary", "fingerprint": "fde5988f1ed7583b", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-1874586782b986ee", "level": "none", "message": {"text": "Commented-code block (6 lines) in packages/client/src/features/phonics-learner/components/KoreanPhonicsActivityPage.tsx:139"}, "properties": {"repobilityId": "0177a59f60c39d53", "scanner": "scanner-primary", "fingerprint": "1874586782b986ee", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-5b8d5b47b8903874", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibrary.ts:14"}, "properties": {"repobilityId": "a83f8380b4dae8c7", "scanner": "scanner-primary", "fingerprint": "5b8d5b47b8903874", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-831ac1ea3f8e1ec6", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.ts:12"}, "properties": {"repobilityId": "f146dc8de3cb3545", "scanner": "scanner-primary", "fingerprint": "831ac1ea3f8e1ec6", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4696b84ca8a92aa8", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202"}, "properties": {"repobilityId": "a3de87ee19ad6f7d", "scanner": "scanner-primary", "fingerprint": "4696b84ca8a92aa8", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ef3c3e76ac945633", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/client/src/features/games/hooks/usePhonicsMap.ts:7"}, "properties": {"repobilityId": "d5c6afa2a80be660", "scanner": "scanner-primary", "fingerprint": "ef3c3e76ac945633", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-57118c65ad52a841", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/games/hooks/usePhonicsMap.ts:77"}, "properties": {"repobilityId": "6f0561a937ace940", "scanner": "scanner-primary", "fingerprint": "57118c65ad52a841", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-dc09104ed01a0a55", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/auth/api/auth.api.ts:59"}, "properties": {"repobilityId": "3129dd66134d377e", "scanner": "scanner-primary", "fingerprint": "dc09104ed01a0a55", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-b803744e861d6eb4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/viewer/components/GameListViewer.tsx:52"}, "properties": {"repobilityId": "df678581dc7cadfa", "scanner": "scanner-primary", "fingerprint": "b803744e861d6eb4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-c3e3b930a9a169f2", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-strategy-templates.ts:14"}, "properties": {"repobilityId": "8fe01a40996a17e4", "scanner": "scanner-primary", "fingerprint": "c3e3b930a9a169f2", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-295711b7e503e6a6", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/api/use-r2-upload.ts:10"}, "properties": {"repobilityId": "6b19de104099dfe7", "scanner": "scanner-primary", "fingerprint": "295711b7e503e6a6", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-017b45207d50e9b0", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/client/src/features/marketing/api/use-analytics.ts:14"}, "properties": {"repobilityId": "9deb10eb1b84e5a2", "scanner": "scanner-primary", "fingerprint": "017b45207d50e9b0", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-bcd1aad8b98d2cfa", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/strategy/StrategyImportDialog.tsx:63"}, "properties": {"repobilityId": "38d908bbe6d9d427", "scanner": "scanner-primary", "fingerprint": "bcd1aad8b98d2cfa", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-15afc0bf7fba2dd8", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/components/analytics/seo/SeoDashboard.tsx:60"}, "properties": {"repobilityId": "d7220b331558337e", "scanner": "scanner-primary", "fingerprint": "15afc0bf7fba2dd8", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a19108f3d672f62d", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/client/src/features/marketing/hooks/use-channel-translation.ts:28"}, "properties": {"repobilityId": "b661e9f941a3e182", "scanner": "scanner-primary", "fingerprint": "a19108f3d672f62d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ae85f1a4bf26c490", "level": "note", "message": {"text": "Legacy-named symbol `__legacy` in packages/client/src/features/longform-video/lib/group-by-style.ts:3"}, "properties": {"repobilityId": "06aac094e914ae7a", "scanner": "scanner-primary", "fingerprint": "ae85f1a4bf26c490", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-b373ac64c91675c5", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/client/src/features/phonics/components/LetterWritingCanvas.tsx:297"}, "properties": {"repobilityId": "9c46ae174d5ad0db", "scanner": "scanner-primary", "fingerprint": "b373ac64c91675c5", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-a8a5213b39c7df24", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/convert-phonics-book1-illustrations-to-webp.mjs:94"}, "properties": {"repobilityId": "143db286aec2da3f", "scanner": "scanner-primary", "fingerprint": "a8a5213b39c7df24", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f3cee18d3fd2a1d5", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/server/scripts/cleanup-l4-leftovers.mjs:2"}, "properties": {"repobilityId": "4ad61ec4729ca469", "scanner": "scanner-primary", "fingerprint": "f3cee18d3fd2a1d5", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-122324a6cb42687b", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/scan-untranslated-keyobjects.mjs:16"}, "properties": {"repobilityId": "320c5df982150cb8", "scanner": "scanner-primary", "fingerprint": "122324a6cb42687b", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e28264fb8aa8237e", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-points-book1.mjs:546"}, "properties": {"repobilityId": "2adff91bbe594a4a", "scanner": "scanner-primary", "fingerprint": "e28264fb8aa8237e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d02021f7de7754e6", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/verify-key-object-in-page-text.mjs:35"}, "properties": {"repobilityId": "b2e8a2393108a682", "scanner": "scanner-primary", "fingerprint": "d02021f7de7754e6", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-9491c9504c5207fb", "level": "note", "message": {"text": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-key-objects-wiktionary.mjs:61"}, "properties": {"repobilityId": "fb4b90505ae91668", "scanner": "scanner-primary", "fingerprint": "9491c9504c5207fb", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-155c5881593b0319", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-key-objects-strict-ko.mjs:146"}, "properties": {"repobilityId": "cf94e5278363a87b", "scanner": "scanner-primary", "fingerprint": "155c5881593b0319", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-5c0533f26b65d04e", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/sync-phonics-book1-targetwords.mjs:35"}, "properties": {"repobilityId": "63f754f224326845", "scanner": "scanner-primary", "fingerprint": "5c0533f26b65d04e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-092e453811f86313", "level": "note", "message": {"text": "Legacy-named symbol `rest_v1` in packages/server/scripts/classify-by-page-text.mjs:81"}, "properties": {"repobilityId": "88841b2fc87c709d", "scanner": "scanner-primary", "fingerprint": "092e453811f86313", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-4bf1fa8270322915", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/classify-by-page-text.mjs:188"}, "properties": {"repobilityId": "85635f884fc652c9", "scanner": "scanner-primary", "fingerprint": "4bf1fa8270322915", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-02f0853c4c95873a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/backfill-phonics-book1-flashcard-phonemes.mjs:30"}, "properties": {"repobilityId": "2f8770fda88b0f76", "scanner": "scanner-primary", "fingerprint": "02f0853c4c95873a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a3280609726a3e0a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/research-keyword-plans.mjs:63"}, "properties": {"repobilityId": "a2da4ee6a432527a", "scanner": "scanner-primary", "fingerprint": "a3280609726a3e0a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-578f9fffbef5994f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/seed-letter-tracing-strokes-book1.mjs:354"}, "properties": {"repobilityId": "b2b7a83fc2b10f5c", "scanner": "scanner-primary", "fingerprint": "578f9fffbef5994f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f03d372d16062848", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/scripts/dump-non-noun-key-objects.mjs:66"}, "properties": {"repobilityId": "29ef2e45d7a43228", "scanner": "scanner-primary", "fingerprint": "f03d372d16062848", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ad0ba1f757f7bf61", "level": "note", "message": {"text": "Legacy-named symbol `totalOld` in packages/server/src/scripts/migrate-assets.ts:35"}, "properties": {"repobilityId": "b28f7c9b5f6f6b90", "scanner": "scanner-primary", "fingerprint": "ad0ba1f757f7bf61", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-d9a0d36078d150db", "level": "none", "message": {"text": "Commented-code block (18 lines) in packages/server/src/utils/book-v2-keys.ts:3"}, "properties": {"repobilityId": "60a48833cf7c8d9d", "scanner": "scanner-primary", "fingerprint": "d9a0d36078d150db", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-8aca41e24b3c3ca9", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/grok.provider.ts:75"}, "properties": {"repobilityId": "dba8825097c45e41", "scanner": "scanner-primary", "fingerprint": "8aca41e24b3c3ca9", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-80286bb3eced3afd", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/providers/naver.provider.ts:38"}, "properties": {"repobilityId": "c3d782c50dea9f8a", "scanner": "scanner-primary", "fingerprint": "80286bb3eced3afd", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d3becfe9d53e8746", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/book-v2.service.ts:323"}, "properties": {"repobilityId": "3920b3597c05de05", "scanner": "scanner-primary", "fingerprint": "d3becfe9d53e8746", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-1ff3eaf6a2c8e044", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/publish.service.ts:79"}, "properties": {"repobilityId": "ebc658a4db4e1760", "scanner": "scanner-primary", "fingerprint": "1ff3eaf6a2c8e044", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4d5652c4d409a756", "level": "none", "message": {"text": "Commented-code block (5 lines) in packages/server/src/services/mkt/__tests__/publish-scheduler.service.test.ts:10"}, "properties": {"repobilityId": "bdaa55dfab5ac401", "scanner": "scanner-primary", "fingerprint": "4d5652c4d409a756", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-aecca1e0d4455a23", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/ga4.ts:107"}, "properties": {"repobilityId": "19da04b92c6e7f41", "scanner": "scanner-primary", "fingerprint": "aecca1e0d4455a23", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d0cfa37ce098efc7", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 packages/server/src/services/mkt/external/youtube-data.ts:120"}, "properties": {"repobilityId": "55419c15c9b00246", "scanner": "scanner-primary", "fingerprint": "d0cfa37ce098efc7", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-cc0f9d8e41b33b0e", "level": "note", "message": {"text": "11 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "c82cd8cd626c3599", "scanner": "scanner-primary", "fingerprint": "cc0f9d8e41b33b0e", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-fd6ca0aaf987a2e6", "level": "warning", "message": {"text": "Frontend route `/marketing/*` has no Link/navigate to it \u2014 packages/client/src/features/marketing/pages/MarketingLayout.test.tsx"}, "properties": {"repobilityId": "a6bdf2ad357e86b5", "scanner": "scanner-primary", "fingerprint": "fd6ca0aaf987a2e6", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-1fc47e9321235cd2", "level": "error", "message": {"text": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/scripts/classify-key-objects-wiktionary.mjs:60)"}, "properties": {"repobilityId": "e462fccc2b56b53c", "scanner": "scanner-primary", "fingerprint": "1fc47e9321235cd2", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-5aab9815412c6708", "level": "error", "message": {"text": "Dangling fetch: GET https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(key)} (packages/server/scripts/classify-key-objects-en.mjs:50)"}, "properties": {"repobilityId": "908273219b78525d", "scanner": "scanner-primary", "fingerprint": "5aab9815412c6708", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-d719978a4c9306bf", "level": "error", "message": {"text": "Dangling fetch: GET https://en.wiktionary.org/api/rest_v1/page/definition/${encodeURIComponent(key)} (packages/server/scripts/classify-by-page-text.mjs:80)"}, "properties": {"repobilityId": "0f07072fdb32adc1", "scanner": "scanner-primary", "fingerprint": "d719978a4c9306bf", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-83a227179cd30fa1", "level": "error", "message": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/scripts/research-keyword-plans.mjs:63)"}, "properties": {"repobilityId": "c06a31713f67e03a", "scanner": "scanner-primary", "fingerprint": "83a227179cd30fa1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-eeae7092e14026fd", "level": "error", "message": {"text": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:70)"}, "properties": {"repobilityId": "408dd9373de29616", "scanner": "scanner-primary", "fingerprint": "eeae7092e14026fd", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-20979a6a2bce6be7", "level": "error", "message": {"text": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:146)"}, "properties": {"repobilityId": "cddab3dfb0da5794", "scanner": "scanner-primary", "fingerprint": "20979a6a2bce6be7", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-1417cb44c17a9f57", "level": "error", "message": {"text": "Dangling fetch: PUT /api/korean-jamo-stroke-library (packages/client/src/pages/KoreanJamoStrokeBulkEditorPage.tsx:272)"}, "properties": {"repobilityId": "532e1134a8fff947", "scanner": "scanner-primary", "fingerprint": "1417cb44c17a9f57", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-59d910b3f44cb79b", "level": "error", "message": {"text": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:63)"}, "properties": {"repobilityId": "389986f959baba70", "scanner": "scanner-primary", "fingerprint": "59d910b3f44cb79b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-daa6ffcd69ec72a2", "level": "error", "message": {"text": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:190)"}, "properties": {"repobilityId": "dfb4d63dbc4b00c3", "scanner": "scanner-primary", "fingerprint": "daa6ffcd69ec72a2", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-bcc24d748c28d538", "level": "error", "message": {"text": "Dangling fetch: PUT /api/letter-stroke-library (packages/client/src/pages/LetterStrokeBulkEditorPage.tsx:311)"}, "properties": {"repobilityId": "c826da66d640d9c8", "scanner": "scanner-primary", "fingerprint": "bcc24d748c28d538", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-a4277bbd9e7fe714", "level": "error", "message": {"text": "Dangling fetch: GET /api/korean-jamo-stroke-library (packages/client/src/features/phonics-learner/hooks/useKoreanJamoStrokeLibrary.ts:14)"}, "properties": {"repobilityId": "1d31b491f87f062b", "scanner": "scanner-primary", "fingerprint": "a4277bbd9e7fe714", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-3fe3ffbebbbe7a32", "level": "error", "message": {"text": "Dangling fetch: GET /api/letter-stroke-library (packages/client/src/features/phonics-learner/hooks/useLetterStrokeLibrary.ts:12)"}, "properties": {"repobilityId": "8f32e4057f665a74", "scanner": "scanner-primary", "fingerprint": "3fe3ffbebbbe7a32", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-e132bd289bcba02b", "level": "error", "message": {"text": "Dangling fetch: POST /api/speaking/transcribe (packages/client/src/features/games/hooks/useSpeechRecognizer.ts:202)"}, "properties": {"repobilityId": "e1c8014b746c387c", "scanner": "scanner-primary", "fingerprint": "e132bd289bcba02b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-dbcf45c1e31bf6ab", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/keywords/recommend (packages/client/src/features/marketing/api/use-golden-keywords.ts:25)"}, "properties": {"repobilityId": "2a6dc56d924d8d98", "scanner": "scanner-primary", "fingerprint": "dbcf45c1e31bf6ab", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-9521849c87ae1658", "level": "error", "message": {"text": "Dangling fetch: GET /api/mkt/strategy/templates (packages/client/src/features/marketing/api/use-strategy-templates.ts:14)"}, "properties": {"repobilityId": "bdfea68c461c01a4", "scanner": "scanner-primary", "fingerprint": "9521849c87ae1658", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-3a6a7e10e63d499e", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/storage/presign (packages/client/src/features/marketing/api/use-r2-upload.ts:36)"}, "properties": {"repobilityId": "e69e61113bd49bd4", "scanner": "scanner-primary", "fingerprint": "3a6a7e10e63d499e", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-8440d7b599d77a38", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/analytics/meta-insights (packages/client/src/features/marketing/api/use-analytics.ts:163)"}, "properties": {"repobilityId": "aa3a052f8f026b10", "scanner": "scanner-primary", "fingerprint": "8440d7b599d77a38", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-26a875f5c9c807dd", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/analytics/youtube-channel (packages/client/src/features/marketing/api/use-analytics.ts:224)"}, "properties": {"repobilityId": "ba6ac9e88f97cb2e", "scanner": "scanner-primary", "fingerprint": "26a875f5c9c807dd", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f2c00e39d3b1f053", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/components/content/BaseArticlePanel.tsx:133)"}, "properties": {"repobilityId": "5f71332d05a5aa7a", "scanner": "scanner-primary", "fingerprint": "f2c00e39d3b1f053", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-1d2ac1047772f82b", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/BgmSection.tsx:79)"}, "properties": {"repobilityId": "11abe5feab152a8f", "scanner": "scanner-primary", "fingerprint": "1d2ac1047772f82b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-62f793eb15b5d89b", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/extract-text (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:71)"}, "properties": {"repobilityId": "06df954ff18e13d4", "scanner": "scanner-primary", "fingerprint": "62f793eb15b5d89b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-82c18527c2a14dc2", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/mkt/storage/delete (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:118)"}, "properties": {"repobilityId": "7753ee0eb7d6563b", "scanner": "scanner-primary", "fingerprint": "82c18527c2a14dc2", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-993cf9f84f576c45", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/analyze-references (packages/client/src/features/marketing/components/project/sections/ReferenceFilesSection.tsx:141)"}, "properties": {"repobilityId": "7c55b73a4ec18866", "scanner": "scanner-primary", "fingerprint": "993cf9f84f576c45", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-789df64cb5c20f9a", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/generate (packages/client/src/features/marketing/hooks/use-ai-generation.ts:23)"}, "properties": {"repobilityId": "407c3e2c470229f5", "scanner": "scanner-primary", "fingerprint": "789df64cb5c20f9a", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-27bbe76405eab5b7", "level": "error", "message": {"text": "Dangling fetch: GET /api/mkt/storage/proxy?url=${encodeURIComponent(url)} (packages/client/src/features/marketing/hooks/use-channel-translation.ts:28)"}, "properties": {"repobilityId": "094298da38e470f8", "scanner": "scanner-primary", "fingerprint": "27bbe76405eab5b7", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-ca24559425e01984", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/hooks/use-image-generation.ts:14)"}, "properties": {"repobilityId": "e3c87f75dc7a723c", "scanner": "scanner-primary", "fingerprint": "ca24559425e01984", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-b8bce4582851e7b7", "level": "error", "message": {"text": "Dangling fetch: POST /api/mkt/ai/generate-image (packages/client/src/features/marketing/store/batch-image-store.ts:102)"}, "properties": {"repobilityId": "c932ccca3e5491e5", "scanner": "scanner-primary", "fingerprint": "b8bce4582851e7b7", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-96c98cb6d3d49399", "level": "error", "message": {"text": "Dangling fetch: GET https://api.x.ai/v1/videos/${requestId} (packages/server/src/providers/grok.provider.ts:35)"}, "properties": {"repobilityId": "a9ae18d0b63ac632", "scanner": "scanner-primary", "fingerprint": "96c98cb6d3d49399", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-ea923d64a6622dca", "level": "error", "message": {"text": "Dangling fetch: POST https://api.x.ai/v1/videos/generations (packages/server/src/providers/grok.provider.ts:126)"}, "properties": {"repobilityId": "d9ced7d8e5ca86db", "scanner": "scanner-primary", "fingerprint": "ea923d64a6622dca", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-478dae54305529d5", "level": "error", "message": {"text": "Dangling fetch: GET https://graph.facebook.com/v21.0/ig_hashtag_search?q=${encodeURIComponent(\n      keyword\n    )}&user_id=${encodeURIComponent(igUserId)}&access_token=${accessToken} (packages/server/src/services/mkt/monitoring.service.ts:343)"}, "properties": {"repobilityId": "143b1b764134b850", "scanner": "scanner-primary", "fingerprint": "478dae54305529d5", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-5ec598b427c5c9c1", "level": "error", "message": {"text": "Dangling fetch: GET https://graph.facebook.com/v21.0/${encodeURIComponent(\n      hashtagId\n    )}/recent_media?user_id=${encodeURIComponent(\n      igUserId\n    )}&fields=id,caption,media_type,media_url,permalink,timestamp,like_count,comments_count&access_token=${accessToken} (packages/server/src/ser"}, "properties": {"repobilityId": "8402854ee7bd03ca", "scanner": "scanner-primary", "fingerprint": "5ec598b427c5c9c1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-34845ec2d6c42166", "level": "error", "message": {"text": "Dangling fetch: POST https://oauth2.googleapis.com/token (packages/server/src/services/mkt/external/ga4.ts:107)"}, "properties": {"repobilityId": "75bcadb6a54c0548", "scanner": "scanner-primary", "fingerprint": "34845ec2d6c42166", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f455f8956c4bf723", "level": "error", "message": {"text": "Dangling fetch: POST https://analyticsdata.googleapis.com/v1beta/properties/${cfg.propertyId}:runReport (packages/server/src/services/mkt/external/ga4.ts:131)"}, "properties": {"repobilityId": "6c7feac1e5c6819c", "scanner": "scanner-primary", "fingerprint": "f455f8956c4bf723", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-9499afc569e06976", "level": "error", "message": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/keywords_data/google/search_volume/live (packages/server/src/services/mkt/external/dataforseo.ts:95)"}, "properties": {"repobilityId": "990ebd094474f378", "scanner": "scanner-primary", "fingerprint": "9499afc569e06976", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-39ddc19c9740ec33", "level": "error", "message": {"text": "Dangling fetch: POST https://api.dataforseo.com/v3/serp/google/organic/live/advanced (packages/server/src/services/mkt/external/dataforseo.ts:130)"}, "properties": {"repobilityId": "46d3eda5e37ce36c", "scanner": "scanner-primary", "fingerprint": "39ddc19c9740ec33", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-e27de824cf0f3ca2", "level": "note", "message": {"text": "Unused endpoint: POST /images/analyze-style"}, "properties": {"repobilityId": "27091fe826850192", "scanner": "scanner-primary", "fingerprint": "e27de824cf0f3ca2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9abaa7f794c14ad6", "level": "note", "message": {"text": "Unused endpoint: POST /images/upload-audio"}, "properties": {"repobilityId": "40948c91e2de95d1", "scanner": "scanner-primary", "fingerprint": "9abaa7f794c14ad6", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-586532bd75938b2d", "level": "note", "message": {"text": "Unused endpoint: POST /phonics-library/upload"}, "properties": {"repobilityId": "795df140accbcc54", "scanner": "scanner-primary", "fingerprint": "586532bd75938b2d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-6c648649b8c303d9", "level": "note", "message": {"text": "Unused endpoint: POST /system-sounds/upload"}, "properties": {"repobilityId": "17ece6bbff7f6bfd", "scanner": "scanner-primary", "fingerprint": "6c648649b8c303d9", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-d0a2eeada7bcb08c", "level": "note", "message": {"text": "Unused endpoint: POST /images/download-zip"}, "properties": {"repobilityId": "c4084349110252a6", "scanner": "scanner-primary", "fingerprint": "d0a2eeada7bcb08c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-8ab40b02f926b71c", "level": "note", "message": {"text": "Unused endpoint: GET /api/debug/logs"}, "properties": {"repobilityId": "47cbd431b30c0ff6", "scanner": "scanner-primary", "fingerprint": "8ab40b02f926b71c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5b04b741ddbc5ce9", "level": "note", "message": {"text": "Unused endpoint: USE /api/storybooks"}, "properties": {"repobilityId": "5c26151e4c9545c8", "scanner": "scanner-primary", "fingerprint": "5b04b741ddbc5ce9", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5da77a419e1bf1c4", "level": "note", "message": {"text": "Unused endpoint: USE /api/images"}, "properties": {"repobilityId": "80bf5c6a53f449a1", "scanner": "scanner-primary", "fingerprint": "5da77a419e1bf1c4", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-54922513d7dd0abe", "level": "note", "message": {"text": "Unused endpoint: USE /api/tts"}, "properties": {"repobilityId": "7b56fb7a96263437", "scanner": "scanner-primary", "fingerprint": "54922513d7dd0abe", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-48e1f815b5fc2c5c", "level": "note", "message": {"text": "Unused endpoint: USE /api/translation"}, "properties": {"repobilityId": "3b43066e1e745026", "scanner": "scanner-primary", "fingerprint": "48e1f815b5fc2c5c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-70c90579a534e096", "level": "note", "message": {"text": "Unused endpoint: USE /api/quiz"}, "properties": {"repobilityId": "255ccc78b928ece3", "scanner": "scanner-primary", "fingerprint": "70c90579a534e096", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1a1fd97cebed4b2f", "level": "note", "message": {"text": "Unused endpoint: USE /api/audiobooks"}, "properties": {"repobilityId": "85ea8ed93c04a017", "scanner": "scanner-primary", "fingerprint": "1a1fd97cebed4b2f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-67d8b39db0f89cc2", "level": "note", "message": {"text": "Unused endpoint: USE /api/phonics"}, "properties": {"repobilityId": "f24ea126f034f68a", "scanner": "scanner-primary", "fingerprint": "67d8b39db0f89cc2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-bfa15851ef1babd4", "level": "note", "message": {"text": "Unused endpoint: USE /api/games"}, "properties": {"repobilityId": "47a46091dc312526", "scanner": "scanner-primary", "fingerprint": "bfa15851ef1babd4", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-a6afd11065d1a0fc", "level": "note", "message": {"text": "Unused endpoint: USE /api/phonics-library"}, "properties": {"repobilityId": "cc1b7092cf217dd8", "scanner": "scanner-primary", "fingerprint": "a6afd11065d1a0fc", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-8ca4664c9cda1cd6", "level": "note", "message": {"text": "Unused endpoint: USE /api/settings"}, "properties": {"repobilityId": "d52d8e0310adb9b2", "scanner": "scanner-primary", "fingerprint": "8ca4664c9cda1cd6", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-89d9a579767e69a2", "level": "note", "message": {"text": "Unused endpoint: USE /api/system-sounds"}, "properties": {"repobilityId": "6ffdebc337aed539", "scanner": "scanner-primary", "fingerprint": "89d9a579767e69a2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-110ec2983974870e", "level": "note", "message": {"text": "Unused endpoint: USE /api/character-library"}, "properties": {"repobilityId": "d2baf0682bcbab3a", "scanner": "scanner-primary", "fingerprint": "110ec2983974870e", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-c99484be99f591ea", "level": "note", "message": {"text": "Unused endpoint: USE /api/art-style-library"}, "properties": {"repobilityId": "556cef56132e4795", "scanner": "scanner-primary", "fingerprint": "c99484be99f591ea", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9e75e50a0913576a", "level": "note", "message": {"text": "Unused endpoint: USE /api/vocabulary-db"}, "properties": {"repobilityId": "3bb3e31de3c857b9", "scanner": "scanner-primary", "fingerprint": "9e75e50a0913576a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4d2feab9ea8ddbb8", "level": "note", "message": {"text": "Unused endpoint: USE /api/marketing"}, "properties": {"repobilityId": "c312b46c332e13c7", "scanner": "scanner-primary", "fingerprint": "4d2feab9ea8ddbb8", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0e430ecbee20ddd9", "level": "note", "message": {"text": "Unused endpoint: USE /api/prompt-presets"}, "properties": {"repobilityId": "cc781e5607715438", "scanner": "scanner-primary", "fingerprint": "0e430ecbee20ddd9", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b8bff7d9e91a6fbc", "level": "note", "message": {"text": "Unused endpoint: USE /api/longform"}, "properties": {"repobilityId": "5fb16df8e237e2e9", "scanner": "scanner-primary", "fingerprint": "b8bff7d9e91a6fbc", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e489bcb6d411b75b", "level": "note", "message": {"text": "Unused endpoint: USE /api/youtube-presets"}, "properties": {"repobilityId": "442831396877dd44", "scanner": "scanner-primary", "fingerprint": "e489bcb6d411b75b", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0a202014de24cd71", "level": "note", "message": {"text": "Unused endpoint: USE /api/speaking"}, "properties": {"repobilityId": "250b7cd48a41b4a8", "scanner": "scanner-primary", "fingerprint": "0a202014de24cd71", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-6d33b31ec8c97317", "level": "note", "message": {"text": "Unused endpoint: USE /api/admin/book-v2"}, "properties": {"repobilityId": "cbc70f177da8644a", "scanner": "scanner-primary", "fingerprint": "6d33b31ec8c97317", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-3da6ece45fff7553", "level": "note", "message": {"text": "Unused endpoint: USE /api/v2/books"}, "properties": {"repobilityId": "370f200695d93e68", "scanner": "scanner-primary", "fingerprint": "3da6ece45fff7553", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-906072993257effe", "level": "note", "message": {"text": "Unused endpoint: USE /api/hori"}, "properties": {"repobilityId": "21c3ed89301f4cfc", "scanner": "scanner-primary", "fingerprint": "906072993257effe", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5e8f238aa42558c5", "level": "note", "message": {"text": "Unused endpoint: USE /api/playground"}, "properties": {"repobilityId": "a7eeabd7eac4224c", "scanner": "scanner-primary", "fingerprint": "5e8f238aa42558c5", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-cc1c4a8ac4ffe02a", "level": "note", "message": {"text": "Unused endpoint: USE /api/vocabulary-units"}, "properties": {"repobilityId": "8f63cf2ee3ff747a", "scanner": "scanner-primary", "fingerprint": "cc1c4a8ac4ffe02a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-417f2248ee9ad120", "level": "note", "message": {"text": "Unused endpoint: USE /api/library-config"}, "properties": {"repobilityId": "5915df224ad7be22", "scanner": "scanner-primary", "fingerprint": "417f2248ee9ad120", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b991bde539207c9b", "level": "note", "message": {"text": "Unused endpoint: USE /api/vocab-overrides"}, "properties": {"repobilityId": "a2b4980290ce7c57", "scanner": "scanner-primary", "fingerprint": "b991bde539207c9b", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-135b1b5d467bf8ec", "level": "note", "message": {"text": "Unused endpoint: USE /api/letter-stroke-library"}, "properties": {"repobilityId": "37dacc68e0d1fee5", "scanner": "scanner-primary", "fingerprint": "135b1b5d467bf8ec", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-cfd0c4c99d7b0237", "level": "note", "message": {"text": "Unused endpoint: USE /api/korean-jamo-stroke-library"}, "properties": {"repobilityId": "b6af211b72da9c20", "scanner": "scanner-primary", "fingerprint": "cfd0c4c99d7b0237", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e4dd14116a017b24", "level": "note", "message": {"text": "Unused endpoint: USE /api/mkt"}, "properties": {"repobilityId": "fe12f1bff9ec317b", "scanner": "scanner-primary", "fingerprint": "e4dd14116a017b24", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f0e081a44d033b2a", "level": "note", "message": {"text": "Unused endpoint: GET /api/r2-proxy"}, "properties": {"repobilityId": "4c66d91aa5928b4f", "scanner": "scanner-primary", "fingerprint": "f0e081a44d033b2a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-77a21e69b379a5bf", "level": "note", "message": {"text": "Unused endpoint: GET /{*path}"}, "properties": {"repobilityId": "4960574ae8b8070d", "scanner": "scanner-primary", "fingerprint": "77a21e69b379a5bf", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-34a0d470c601d97f", "level": "note", "message": {"text": "Unused endpoint: GET /catalog"}, "properties": {"repobilityId": "c69ebf2e2017978b", "scanner": "scanner-primary", "fingerprint": "34a0d470c601d97f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7e2aebb8dfde31c6", "level": "note", "message": {"text": "Unused endpoint: POST /items"}, "properties": {"repobilityId": "742d7338e8fe9a2c", "scanner": "scanner-primary", "fingerprint": "7e2aebb8dfde31c6", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5baa8971ebe192a1", "level": "note", "message": {"text": "Unused endpoint: GET /"}, "properties": {"repobilityId": "c8f68816ea27682d", "scanner": "scanner-primary", "fingerprint": "5baa8971ebe192a1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e305f58e0a58346e", "level": "note", "message": {"text": "Unused endpoint: GET /word/:word"}, "properties": {"repobilityId": "dd0ef17837c3e983", "scanner": "scanner-primary", "fingerprint": "e305f58e0a58346e", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-97602644d814e467", "level": "note", "message": {"text": "Unused endpoint: GET /storybook/:storybookId"}, "properties": {"repobilityId": "ca0aaae1a649c095", "scanner": "scanner-primary", "fingerprint": "97602644d814e467", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fa413208496243a5", "level": "note", "message": {"text": "Unused endpoint: POST /sync"}, "properties": {"repobilityId": "be978478642208d4", "scanner": "scanner-primary", "fingerprint": "fa413208496243a5", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0fba7734c9f573f3", "level": "note", "message": {"text": "Unused endpoint: POST /bulk-sync"}, "properties": {"repobilityId": "3acf1e8b2308a362", "scanner": "scanner-primary", "fingerprint": "0fba7734c9f573f3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0a71b65913e31f1b", "level": "note", "message": {"text": "Unused endpoint: DELETE /reset"}, "properties": {"repobilityId": "f276b04c0c82e96d", "scanner": "scanner-primary", "fingerprint": "0a71b65913e31f1b", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fd2c7e47d6c67bab", "level": "note", "message": {"text": "Unused endpoint: POST /word-pool/enrich"}, "properties": {"repobilityId": "e5175db2d1107160", "scanner": "scanner-primary", "fingerprint": "fd2c7e47d6c67bab", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-52362b8f59ba18c0", "level": "note", "message": {"text": "Unused endpoint: GET /word-pool/sample"}, "properties": {"repobilityId": "0a1c48fb084bfa62", "scanner": "scanner-primary", "fingerprint": "52362b8f59ba18c0", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5e30f8198fa3c7b0", "level": "note", "message": {"text": "Unused endpoint: GET /word-pool/by-categories"}, "properties": {"repobilityId": "4dbdb00a42135407", "scanner": "scanner-primary", "fingerprint": "5e30f8198fa3c7b0", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-03539564056e5279", "level": "note", "message": {"text": "Unused endpoint: POST /blog/generate"}, "properties": {"repobilityId": "a5b7d345cfa8e112", "scanner": "scanner-primary", "fingerprint": "03539564056e5279", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-59d7c3251efeaba8", "level": "note", "message": {"text": "Unused endpoint: POST /blog/generate-config"}, "properties": {"repobilityId": "c3f09a0ec31786eb", "scanner": "scanner-primary", "fingerprint": "59d7c3251efeaba8", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}