{"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-7fc11f6c429fae02", "name": "Stray `console.log` in TS/JS \u2014 server/claude-access.js:22", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/claude-access.js:22"}, "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-de2ad661495eff5b", "name": "Stray `console.log` in TS/JS \u2014 server/index.js:41", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/index.js:41"}, "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-00649e164c960046", "name": "Stray `console.log` in TS/JS \u2014 server/set-admin.js:31", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/set-admin.js:31"}, "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-cabd5441ece02111", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/scrapeCuBadges.js:293", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/scrapeCuBadges.js:293"}, "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-989f50d0f57cc9b2", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/refreshScrapedBadges.js:89", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/refreshScrapedBadges.js:89"}, "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-6d7cc4a275f91f62", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeDescriptions.js:91", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeDescriptions.js:91"}, "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-c3c15286469f605f", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeEmojis.js:83", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeEmojis.js:83"}, "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-3d2585899cf86e8b", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/parseScrapedTexts.js:424", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/parseScrapedTexts.js:424"}, "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-581cc78fa130e224", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/reparseFromCache.js:41", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/reparseFromCache.js:41"}, "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-8a24ba6d5b0d4d63", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadgeLibrary.js:46", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadgeLibrary.js:46"}, "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-6d60255ef3e77295", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/backfillEmojiImages.js:93", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/backfillEmojiImages.js:93"}, "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-382c64e2a7d7be25", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeSitemap.js:44", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeSitemap.js:44"}, "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-802aede069be7153", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/importAwards.js:550", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importAwards.js:550"}, "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-c26fcfa3728f13a4", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/mergeScrapedBadges.js:114", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/mergeScrapedBadges.js:114"}, "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-30c0cc4118d9cda8", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeLibrary.js:144", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeLibrary.js:144"}, "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-811231e5efd6950d", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/cropBadgeImages.js:43", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/cropBadgeImages.js:43"}, "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-db18ae79df8318ac", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/fixMisattributedBadges.js:31", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/fixMisattributedBadges.js:31"}, "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-eedfbb4f0f1b19c8", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/exportBadgeLibrary.js:69", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/exportBadgeLibrary.js:69"}, "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-2ba5b7cd7b924a4d", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadges.js:90", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadges.js:90"}, "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-2d667ec98b2f2637", "name": "Stray `console.log` in TS/JS \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:635", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:635"}, "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-4c6463c6ce25e5bf", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:33", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:33"}, "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-5a0482d80f866b56", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/dockerService.js:74", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/dockerService.js:74"}, "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-fb73c28e21482783", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/wsService.js:6", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/wsService.js: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-87c9e2a72946f8e0", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/roomManager.js:239", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/roomManager.js:239"}, "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-1a16286dcd867b31", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/multiplayerWs.js:57", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/multiplayerWs.js:57"}, "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-416cc59184dc9da9", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/DashboardTable.jsx:74", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/DashboardTable.jsx:74"}, "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-4fc6099ccd3f3ea2", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/TicketBlast.jsx:109", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/TicketBlast.jsx:109"}, "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-049400d460c1993e", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/badges/BadgeBrowser.jsx:231", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/badges/BadgeBrowser.jsx:231"}, "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-843cadcf4861bc8e", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreTemplateList.jsx:61", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreTemplateList.jsx:61"}, "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-0c8ede97a0290ace", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreItem.jsx:124", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreItem.jsx:124"}, "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-e7524be431a5d849", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/shared/Layout.jsx:800", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/shared/Layout.jsx:800"}, "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-23829f91496e4c9f", "name": "Icon-only button without accessible name \u2014 client/src/components/shared/Modal.jsx:35", "shortDescription": {"text": "Icon-only button without accessible name \u2014 client/src/components/shared/Modal.jsx:35"}, "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-3674627a8313d869", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/awards/LinkedBadgePicker.jsx:115", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/awards/LinkedBadgePicker.jsx:115"}, "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-8de3188f20d953c1", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/CurrencyWorkNotice.jsx:73", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/CurrencyWorkNotice.jsx:73"}, "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-fb921f909e51cca3", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/UnifiedBankDialog.jsx:114", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/UnifiedBankDialog.jsx: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-6d61d19b273a49b4", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/TransactionList.jsx:43", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/TransactionList.jsx:43"}, "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-532bb20ee1dd96e9", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidOverviewPage.jsx:421", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidOverviewPage.jsx:421"}, "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-07df9336e8c3d6bf", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTurnsPage.jsx:87", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTurnsPage.jsx:87"}, "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-41a0a84d9f90b73b", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TaskSetDetailPage.jsx:400", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TaskSetDetailPage.jsx:400"}, "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-d7f949e74d5bdba5", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxPage.jsx:214", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxPage.jsx:214"}, "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-1cf42d9d8e83f1eb", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidChoresPage.jsx:128", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidChoresPage.jsx:128"}, "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-0c5a17d3a14b6f97", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidGroupPage.jsx:163", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidGroupPage.jsx:163"}, "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-a7f2cbcdd5f50430", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AwardTreePage.jsx:286", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AwardTreePage.jsx:286"}, "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-dc8b38e8d633eb83", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TurnDetailPage.jsx:195", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TurnDetailPage.jsx:195"}, "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-d9123c8a3fcbafb6", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsChoresPage.jsx:320", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsChoresPage.jsx:320"}, "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-c0cbbe1bee10f513", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTrophiesPage.jsx:173", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTrophiesPage.jsx:173"}, "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-61d1630454884533", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsCommonChoresPage.jsx:55", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsCommonChoresPage.jsx:55"}, "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-068910c65cfedf3f", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidBankPage.jsx:254", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidBankPage.jsx:254"}, "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-94774d9285169be4", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SharedTaskSetsPage.jsx:108", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SharedTaskSetsPage.jsx:108"}, "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-661511d78b7f53b3", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTicketsPage.jsx:87", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTicketsPage.jsx:87"}, "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-c8a5ebb43bd532cc", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AppsPage.jsx:40", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AppsPage.jsx:40"}, "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-608e2859a84ffb59", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTasksPage.jsx:272", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTasksPage.jsx: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-fb993fcdb4857d9b", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsUserDetailPage.jsx:346", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsUserDetailPage.jsx:346"}, "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-f95de3f74652c675", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/UserTaskDetailPage.jsx:1162", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/UserTaskDetailPage.jsx:1162"}, "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-b1338fb12b4a5f6b", "name": "TODO/FIXME marker in shipping code \u2014 client/src/pages/UserTaskDetailPage.jsx:1013", "shortDescription": {"text": "TODO/FIXME marker in shipping code \u2014 client/src/pages/UserTaskDetailPage.jsx:1013"}, "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-47516f5dd9cda88f", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsBadgesPage.jsx:220", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsBadgesPage.jsx:220"}, "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-0d1cf1785cd685a3", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AdminPage.jsx:259", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AdminPage.jsx:259"}, "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-23940af0e4242299", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxKidPage.jsx:140", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxKidPage.jsx:140"}, "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-5e1ecb23cc0b7e7c", "name": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTasksPage.jsx:492", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTasksPage.jsx:492"}, "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-3a5b35f6a5917300", "name": "TODO/FIXME marker in shipping code \u2014 client/dev-dist/workbox-5a5d9309.js:787", "shortDescription": {"text": "TODO/FIXME marker in shipping code \u2014 client/dev-dist/workbox-5a5d9309.js:787"}, "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-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-835eae4c7f17a2f8", "name": "Docker base image is tag-pinned but not digest-pinned: node:22-alpine", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: node:22-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-2c292b667c5cde44", "name": "Docker base image is tag-pinned but not digest-pinned: node:22-slim", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: node:22-slim"}, "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-7a17b21c3e5625a4", "name": "Insecure pattern 'node_child_process' in server/scripts/importAwards.js:17", "shortDescription": {"text": "Insecure pattern 'node_child_process' in server/scripts/importAwards.js:17"}, "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-01ec91fa74643b93", "name": "Insecure pattern 'node_child_process' in server/scripts/cropBadgeImages.js:28", "shortDescription": {"text": "Insecure pattern 'node_child_process' in server/scripts/cropBadgeImages.js:28"}, "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-2ef0ffbbbd54a888", "name": "Insecure pattern 'direct_innerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:37", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:37"}, "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-cfa2bc4f5e73d4e0", "name": "Insecure pattern 'direct_outerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:633", "shortDescription": {"text": "Insecure pattern 'direct_outerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:633"}, "fullDescription": {"text": "Found a known-risky pattern (direct_outerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d06a0ef3178bac48", "name": "Insecure pattern 'cors_wildcard' in server/src/app.js:151", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in server/src/app.js:151"}, "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-9c6ce957ecdec0ec", "name": "Insecure pattern 'direct_innerhtml_assignment' in server/src/sdk/multiplayer.js:496", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in server/src/sdk/multiplayer.js:496"}, "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-d805794d1712edcf", "name": "Insecure pattern 'direct_innerhtml_assignment' in client/src/components/claude/KidWorkspace.jsx:89", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in client/src/components/claude/KidWorkspace.jsx:89"}, "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-3d9e5fdaa2c4363a", "name": "Very large file: server/src/routes/userTasks.js (1631 lines)", "shortDescription": {"text": "Very large file: server/src/routes/userTasks.js (1631 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-a56eced344632307", "name": "Very large file: client/src/components/shared/Layout.jsx (1291 lines)", "shortDescription": {"text": "Very large file: client/src/components/shared/Layout.jsx (1291 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-49e1efd94d9e1f19", "name": "Very large file: client/src/components/bank/MoneyPopover.jsx (1750 lines)", "shortDescription": {"text": "Very large file: client/src/components/bank/MoneyPopover.jsx (1750 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-bacf69e8e4d768c0", "name": "Very large file: client/src/pages/TaskSetDetailPage.jsx (1126 lines)", "shortDescription": {"text": "Very large file: client/src/pages/TaskSetDetailPage.jsx (1126 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-ae59b3257cf18cc5", "name": "Very large file: client/src/pages/UserTaskDetailPage.jsx (3577 lines)", "shortDescription": {"text": "Very large file: client/src/pages/UserTaskDetailPage.jsx (3577 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-60f845579f845d72", "name": "Very large file: client/dev-dist/workbox-5a5d9309.js (3395 lines)", "shortDescription": {"text": "Very large file: client/dev-dist/workbox-5a5d9309.js (3395 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-6893a6c8b0861585", "name": "Very low test-to-source ratio", "shortDescription": {"text": "Very low test-to-source ratio"}, "fullDescription": {"text": "5 test file(s) for 228 source file(s) (ratio 0.02). Consider adding integration or unit tests for critical paths."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "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-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 145 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 58 placeholder/mock markers across 29 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-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. 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": "low", "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. 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-bea357a6497a2d5d", "name": "Agent authority lacks a verifier contract: CLAUDE.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: 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-2c5f98b152cddb6d", "name": "Agent authority lacks a verifier contract: .claude/launch.json", "shortDescription": {"text": "Agent authority lacks a verifier contract: .claude/launch.json"}, "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-c0731726dac3029c", "name": "Commented-code block (7 lines) in server/scripts/scrapeCuBadges.js:161", "shortDescription": {"text": "Commented-code block (7 lines) in server/scripts/scrapeCuBadges.js:161"}, "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-96454ce12b0f2af3", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/scrapeCuBadges.js:80", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/scrapeCuBadges.js:80"}, "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-7000eebfe3c4f9c3", "name": "Commented-code block (6 lines) in server/scripts/parseScrapedTexts.js:80", "shortDescription": {"text": "Commented-code block (6 lines) in server/scripts/parseScrapedTexts.js:80"}, "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-2315a7b51fd0856b", "name": "Commented-code block (6 lines) in server/scripts/importBadgeLibrary.js:94", "shortDescription": {"text": "Commented-code block (6 lines) in server/scripts/importBadgeLibrary.js:94"}, "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-16c2b0d8f2c19cfa", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/auditBadgeSitemap.js:32", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/auditBadgeSitemap.js:32"}, "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-91d36b7c59888a77", "name": "Commented-code block (8 lines) in server/scripts/importAwards.js:28", "shortDescription": {"text": "Commented-code block (8 lines) in server/scripts/importAwards.js:28"}, "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-4232c53b82a5692f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/importAwards.js:509", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/importAwards.js:509"}, "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-3ea3a61c4e29f31e", "name": "Commented-code block (6 lines) in server/scripts/fixMisattributedBadges.js:76", "shortDescription": {"text": "Commented-code block (6 lines) in server/scripts/fixMisattributedBadges.js:76"}, "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-5390136793d6bc6b", "name": "Commented-code block (10 lines) in server/scripts/importBadges.js:51", "shortDescription": {"text": "Commented-code block (10 lines) in server/scripts/importBadges.js:51"}, "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-a5592694b66dd135", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/workbox-8c29f6e4.js:1", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/workbox-8c29f6e4.js:1"}, "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-9fc2aa906a67d9d9", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:1", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:1"}, "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-5ae2e76fa0b66bb1", "name": "Commented-code block (5 lines) in server/src/app.js:144", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/app.js:144"}, "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-e4ed52cb964a08cd", "name": "Commented-code block (5 lines) in server/src/db/migrations.js:401", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/db/migrations.js:401"}, "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-ad5724c920014a8e", "name": "Commented-code block (5 lines) in server/src/services/dockerService.js:162", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/services/dockerService.js:162"}, "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-f52b064ef4855f2c", "name": "Commented-code block (6 lines) in server/src/services/awardSync.js:41", "shortDescription": {"text": "Commented-code block (6 lines) in server/src/services/awardSync.js:41"}, "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-9ad19f83fb98b867", "name": "Commented-code block (7 lines) in server/src/services/wsService.js:8", "shortDescription": {"text": "Commented-code block (7 lines) in server/src/services/wsService.js:8"}, "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-f927bffa4325221a", "name": "Commented-code block (8 lines) in server/src/routes/badges.js:107", "shortDescription": {"text": "Commented-code block (8 lines) in server/src/routes/badges.js:107"}, "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-ee7f99064e2967c9", "name": "Commented-code block (7 lines) in server/src/routes/claude.js:77", "shortDescription": {"text": "Commented-code block (7 lines) in server/src/routes/claude.js:77"}, "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-c00c3238581bf7fb", "name": "Commented-code block (5 lines) in server/src/routes/activity.js:71", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/activity.js:71"}, "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-cfc000aa77e6b8a6", "name": "Commented-code block (5 lines) in server/src/routes/family.js:66", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/family.js:66"}, "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-791ebcf766407306", "name": "Commented-code block (5 lines) in server/src/routes/userTasks.js:112", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/userTasks.js:112"}, "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-30e5ccd77883f309", "name": "Commented-code block (6 lines) in server/src/routes/auth.js:27", "shortDescription": {"text": "Commented-code block (6 lines) in server/src/routes/auth.js:27"}, "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-c8bcc6cbbb5cc506", "name": "Commented-code block (6 lines) in client/src/debug/eventLog.js:1", "shortDescription": {"text": "Commented-code block (6 lines) in client/src/debug/eventLog.js: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-3f4640c4c5508ba3", "name": "Commented-code block (10 lines) in client/src/utils/appHeight.js:4", "shortDescription": {"text": "Commented-code block (10 lines) in client/src/utils/appHeight.js:4"}, "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-6a4a6e7ed6cd28a8", "name": "Commented-code block (5 lines) in client/src/utils/earnBadgeRef.jsx:1", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/utils/earnBadgeRef.jsx: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-c7649990ef0d82c4", "name": "Commented-code block (6 lines) in client/src/utils/awardProgress.js:1", "shortDescription": {"text": "Commented-code block (6 lines) in client/src/utils/awardProgress.js: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-0cb6923f268923af", "name": "Commented-code block (8 lines) in client/src/hooks/useOverscrollGuard.js:3", "shortDescription": {"text": "Commented-code block (8 lines) in client/src/hooks/useOverscrollGuard.js: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-4e6370dba187e0dc", "name": "Commented-code block (5 lines) in client/src/hooks/useScrollLock.js:31", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/hooks/useScrollLock.js:31"}, "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-dfc4470c5f1f582a", "name": "Commented-code block (7 lines) in client/src/context/AuthContext.jsx:21", "shortDescription": {"text": "Commented-code block (7 lines) in client/src/context/AuthContext.jsx:21"}, "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-b927a04baeb2c1ad", "name": "Commented-code block (8 lines) in client/src/components/badges/BadgeBrowser.jsx:192", "shortDescription": {"text": "Commented-code block (8 lines) in client/src/components/badges/BadgeBrowser.jsx:192"}, "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-156f349ad7c1ba08", "name": "Commented-code block (11 lines) in client/src/components/tasks/TaskSetCard.jsx:119", "shortDescription": {"text": "Commented-code block (11 lines) in client/src/components/tasks/TaskSetCard.jsx:119"}, "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-adeb32764b0f194c", "name": "Commented-code block (7 lines) in client/src/components/shared/Layout.jsx:103", "shortDescription": {"text": "Commented-code block (7 lines) in client/src/components/shared/Layout.jsx:103"}, "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-18454211bdcf17af", "name": "Commented-code block (5 lines) in client/src/components/shared/DebugOverlay.jsx:4", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/components/shared/DebugOverlay.jsx:4"}, "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-2d6a6ca61db2ff0f", "name": "Commented-code block (5 lines) in client/src/components/awards/CountAtLevelAwardDetail.jsx:111", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/components/awards/CountAtLevelAwardDetail.jsx:111"}, "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-f78a2f3ffd9846d3", "name": "Commented-code block (5 lines) in client/src/components/bank/MoneyPopover.jsx:473", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/components/bank/MoneyPopover.jsx:473"}, "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-97c8d688db797dae", "name": "Commented-code block (5 lines) in client/src/pages/KidGroupPage.jsx:106", "shortDescription": {"text": "Commented-code block (5 lines) in client/src/pages/KidGroupPage.jsx:106"}, "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-1a1929b292a2450b", "name": "Commented-code block (9 lines) in client/src/pages/AwardTreePage.jsx:183", "shortDescription": {"text": "Commented-code block (9 lines) in client/src/pages/AwardTreePage.jsx:183"}, "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-e3bdf53020a7c978", "name": "Commented-code block (6 lines) in client/src/pages/KidTasksPage.jsx:93", "shortDescription": {"text": "Commented-code block (6 lines) in client/src/pages/KidTasksPage.jsx:93"}, "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-6d35c475a5b9c014", "name": "Commented-code block (6 lines) in client/src/pages/UserTaskDetailPage.jsx:77", "shortDescription": {"text": "Commented-code block (6 lines) in client/src/pages/UserTaskDetailPage.jsx:77"}, "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-03d4b2a83432a291", "name": "Commented-code block (11 lines) in client/src/constants/badgeLevels.js:1", "shortDescription": {"text": "Commented-code block (11 lines) in client/src/constants/badgeLevels.js: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-74647f8faca9d513", "name": "Commented-code block (7 lines) in client/dev-dist/workbox-5a5d9309.js:2535", "shortDescription": {"text": "Commented-code block (7 lines) in client/dev-dist/workbox-5a5d9309.js:2535"}, "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-c6346559dab44c21", "name": "`fetch()` without try/.catch or AbortSignal \u2014 client/dev-dist/workbox-5a5d9309.js:750", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 client/dev-dist/workbox-5a5d9309.js:750"}, "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-f808a4ef3e1071fb", "name": "14 env vars used in code but missing from .env.example", "shortDescription": {"text": "14 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `ANTHROPIC_API_KEY`, `APPS_HOST`, `CLAUDE_CONTAINER_IMAGE`, `CLAUDE_NETWORK`, `CU_COOKIE`, `DATABASE_PATH`, `JWT_ACCESS_EXPIRES_IN`, `JWT_REFRESH_EXPIRES_IN` + 6 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-38633a2cc8c40293", "name": "Frontend route `/terminal/:userId` has no Link/navigate to it \u2014 client/src/App.jsx", "shortDescription": {"text": "Frontend route `/terminal/:userId` has no Link/navigate to it \u2014 client/src/App.jsx"}, "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-c5460862000b0fb3", "name": "Frontend route `/tasks/:userId/group/:groupKey` has no Link/navigate to it \u2014 client/src/App.jsx", "shortDescription": {"text": "Frontend route `/tasks/:userId/group/:groupKey` has no Link/navigate to it \u2014 client/src/App.jsx"}, "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-426c37d6e72db282", "name": "Frontend route `/tasks/:userId/:taskSetId/tree` has no Link/navigate to it \u2014 client/src/App.jsx", "shortDescription": {"text": "Frontend route `/tasks/:userId/:taskSetId/tree` has no Link/navigate to it \u2014 client/src/App.jsx"}, "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-9cca16177040dbd5", "name": "Frontend route `/badges/:userId` has no Link/navigate to it \u2014 client/src/App.jsx", "shortDescription": {"text": "Frontend route `/badges/:userId` has no Link/navigate to it \u2014 client/src/App.jsx"}, "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-f92eb29f314390c3", "name": "Frontend route `/settings/turns/:id` has no Link/navigate to it \u2014 client/src/App.jsx", "shortDescription": {"text": "Frontend route `/settings/turns/:id` has no Link/navigate to it \u2014 client/src/App.jsx"}, "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}}]}}, "automationDetails": {"id": "repobility/24720"}, "properties": {"repository": "brihogan/familydash", "repoUrl": "https://github.com/brihogan/familydash", "branch": "main"}, "results": [{"ruleId": "scanner-7fc11f6c429fae02", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/claude-access.js:22"}, "properties": {"repobilityId": "30e1937838f7bf94", "scanner": "scanner-primary", "fingerprint": "7fc11f6c429fae02", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-de2ad661495eff5b", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/index.js:41"}, "properties": {"repobilityId": "21e6b3de2b3dd856", "scanner": "scanner-primary", "fingerprint": "de2ad661495eff5b", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-00649e164c960046", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/set-admin.js:31"}, "properties": {"repobilityId": "0321c8bdbd3745fd", "scanner": "scanner-primary", "fingerprint": "00649e164c960046", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-cabd5441ece02111", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/scrapeCuBadges.js:293"}, "properties": {"repobilityId": "2fec18ae89606221", "scanner": "scanner-primary", "fingerprint": "cabd5441ece02111", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-989f50d0f57cc9b2", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/refreshScrapedBadges.js:89"}, "properties": {"repobilityId": "245b6e9bc0e2ca7b", "scanner": "scanner-primary", "fingerprint": "989f50d0f57cc9b2", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-6d7cc4a275f91f62", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeDescriptions.js:91"}, "properties": {"repobilityId": "4de230ec2341184c", "scanner": "scanner-primary", "fingerprint": "6d7cc4a275f91f62", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-c3c15286469f605f", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/generateBadgeEmojis.js:83"}, "properties": {"repobilityId": "0a9f8e69ba2a8994", "scanner": "scanner-primary", "fingerprint": "c3c15286469f605f", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-3d2585899cf86e8b", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/parseScrapedTexts.js:424"}, "properties": {"repobilityId": "1e365dd12507f686", "scanner": "scanner-primary", "fingerprint": "3d2585899cf86e8b", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-581cc78fa130e224", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/reparseFromCache.js:41"}, "properties": {"repobilityId": "b1e22c942f487bf2", "scanner": "scanner-primary", "fingerprint": "581cc78fa130e224", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-8a24ba6d5b0d4d63", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadgeLibrary.js:46"}, "properties": {"repobilityId": "e78ca8d7aac9e0ba", "scanner": "scanner-primary", "fingerprint": "8a24ba6d5b0d4d63", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-6d60255ef3e77295", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/backfillEmojiImages.js:93"}, "properties": {"repobilityId": "ac7b4e9f58ec376b", "scanner": "scanner-primary", "fingerprint": "6d60255ef3e77295", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-382c64e2a7d7be25", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeSitemap.js:44"}, "properties": {"repobilityId": "fc6d02e23ad3f47c", "scanner": "scanner-primary", "fingerprint": "382c64e2a7d7be25", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-802aede069be7153", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importAwards.js:550"}, "properties": {"repobilityId": "7f867613de1c44cb", "scanner": "scanner-primary", "fingerprint": "802aede069be7153", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-c26fcfa3728f13a4", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/mergeScrapedBadges.js:114"}, "properties": {"repobilityId": "facfecbce236589a", "scanner": "scanner-primary", "fingerprint": "c26fcfa3728f13a4", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-30c0cc4118d9cda8", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/auditBadgeLibrary.js:144"}, "properties": {"repobilityId": "752913fc1ad221e9", "scanner": "scanner-primary", "fingerprint": "30c0cc4118d9cda8", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-811231e5efd6950d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/cropBadgeImages.js:43"}, "properties": {"repobilityId": "e94af00b0df8e2e2", "scanner": "scanner-primary", "fingerprint": "811231e5efd6950d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-db18ae79df8318ac", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/fixMisattributedBadges.js:31"}, "properties": {"repobilityId": "791bec60e3dfd2c3", "scanner": "scanner-primary", "fingerprint": "db18ae79df8318ac", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-eedfbb4f0f1b19c8", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/exportBadgeLibrary.js:69"}, "properties": {"repobilityId": "a21d43f817f8c37d", "scanner": "scanner-primary", "fingerprint": "eedfbb4f0f1b19c8", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-2ba5b7cd7b924a4d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/importBadges.js:90"}, "properties": {"repobilityId": "30071b91f0763d3c", "scanner": "scanner-primary", "fingerprint": "2ba5b7cd7b924a4d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-2d667ec98b2f2637", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:635"}, "properties": {"repobilityId": "04e7ce517725c55c", "scanner": "scanner-primary", "fingerprint": "2d667ec98b2f2637", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-4c6463c6ce25e5bf", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:33"}, "properties": {"repobilityId": "5cadffb4a05527bf", "scanner": "scanner-primary", "fingerprint": "4c6463c6ce25e5bf", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-5a0482d80f866b56", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/dockerService.js:74"}, "properties": {"repobilityId": "caf5f8941cbe1024", "scanner": "scanner-primary", "fingerprint": "5a0482d80f866b56", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-fb73c28e21482783", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/wsService.js:6"}, "properties": {"repobilityId": "e5e2fffff422b9d2", "scanner": "scanner-primary", "fingerprint": "fb73c28e21482783", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-87c9e2a72946f8e0", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/roomManager.js:239"}, "properties": {"repobilityId": "89c40c2fbd6696cc", "scanner": "scanner-primary", "fingerprint": "87c9e2a72946f8e0", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-1a16286dcd867b31", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/multiplayerWs.js:57"}, "properties": {"repobilityId": "16173913e3b1e89e", "scanner": "scanner-primary", "fingerprint": "1a16286dcd867b31", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-416cc59184dc9da9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/DashboardTable.jsx:74"}, "properties": {"repobilityId": "c29845840d9aa5d6", "scanner": "scanner-primary", "fingerprint": "416cc59184dc9da9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4fc6099ccd3f3ea2", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/dashboard/TicketBlast.jsx:109"}, "properties": {"repobilityId": "0178888f6f17b258", "scanner": "scanner-primary", "fingerprint": "4fc6099ccd3f3ea2", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-049400d460c1993e", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/badges/BadgeBrowser.jsx:231"}, "properties": {"repobilityId": "b2c38a00d0ce2183", "scanner": "scanner-primary", "fingerprint": "049400d460c1993e", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-843cadcf4861bc8e", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreTemplateList.jsx:61"}, "properties": {"repobilityId": "1c1cafa359f6b482", "scanner": "scanner-primary", "fingerprint": "843cadcf4861bc8e", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0c8ede97a0290ace", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/chores/ChoreItem.jsx:124"}, "properties": {"repobilityId": "4b694755109fef13", "scanner": "scanner-primary", "fingerprint": "0c8ede97a0290ace", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e7524be431a5d849", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/shared/Layout.jsx:800"}, "properties": {"repobilityId": "5d6ad656f5feba5c", "scanner": "scanner-primary", "fingerprint": "e7524be431a5d849", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-23829f91496e4c9f", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 client/src/components/shared/Modal.jsx:35"}, "properties": {"repobilityId": "ddac0cfdf774eb29", "scanner": "scanner-primary", "fingerprint": "23829f91496e4c9f", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-3674627a8313d869", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/awards/LinkedBadgePicker.jsx:115"}, "properties": {"repobilityId": "e21c184a9c820ed4", "scanner": "scanner-primary", "fingerprint": "3674627a8313d869", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-8de3188f20d953c1", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/CurrencyWorkNotice.jsx:73"}, "properties": {"repobilityId": "9061538e0ef157a8", "scanner": "scanner-primary", "fingerprint": "8de3188f20d953c1", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-fb921f909e51cca3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/UnifiedBankDialog.jsx:114"}, "properties": {"repobilityId": "4cdd0efacf6fcc04", "scanner": "scanner-primary", "fingerprint": "fb921f909e51cca3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6d61d19b273a49b4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/components/bank/TransactionList.jsx:43"}, "properties": {"repobilityId": "427d9159f6e71f86", "scanner": "scanner-primary", "fingerprint": "6d61d19b273a49b4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-532bb20ee1dd96e9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidOverviewPage.jsx:421"}, "properties": {"repobilityId": "fe7206eb89c52a4e", "scanner": "scanner-primary", "fingerprint": "532bb20ee1dd96e9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-07df9336e8c3d6bf", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTurnsPage.jsx:87"}, "properties": {"repobilityId": "2e657d8a3a6ac1aa", "scanner": "scanner-primary", "fingerprint": "07df9336e8c3d6bf", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-41a0a84d9f90b73b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TaskSetDetailPage.jsx:400"}, "properties": {"repobilityId": "f324856509f440da", "scanner": "scanner-primary", "fingerprint": "41a0a84d9f90b73b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d7f949e74d5bdba5", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxPage.jsx:214"}, "properties": {"repobilityId": "2b49353d0e074179", "scanner": "scanner-primary", "fingerprint": "d7f949e74d5bdba5", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1cf42d9d8e83f1eb", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidChoresPage.jsx:128"}, "properties": {"repobilityId": "5b986c45ca280bda", "scanner": "scanner-primary", "fingerprint": "1cf42d9d8e83f1eb", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0c5a17d3a14b6f97", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidGroupPage.jsx:163"}, "properties": {"repobilityId": "6273eb4f41914924", "scanner": "scanner-primary", "fingerprint": "0c5a17d3a14b6f97", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a7f2cbcdd5f50430", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AwardTreePage.jsx:286"}, "properties": {"repobilityId": "aa51631404075400", "scanner": "scanner-primary", "fingerprint": "a7f2cbcdd5f50430", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-dc8b38e8d633eb83", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/TurnDetailPage.jsx:195"}, "properties": {"repobilityId": "ed2eafb8c712f454", "scanner": "scanner-primary", "fingerprint": "dc8b38e8d633eb83", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d9123c8a3fcbafb6", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsChoresPage.jsx:320"}, "properties": {"repobilityId": "a8223ed36d421ddc", "scanner": "scanner-primary", "fingerprint": "d9123c8a3fcbafb6", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c0cbbe1bee10f513", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTrophiesPage.jsx:173"}, "properties": {"repobilityId": "3f45774c789d62ae", "scanner": "scanner-primary", "fingerprint": "c0cbbe1bee10f513", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-61d1630454884533", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsCommonChoresPage.jsx:55"}, "properties": {"repobilityId": "5c1a0b16f8e07db6", "scanner": "scanner-primary", "fingerprint": "61d1630454884533", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-068910c65cfedf3f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidBankPage.jsx:254"}, "properties": {"repobilityId": "2578d71c73dd65c7", "scanner": "scanner-primary", "fingerprint": "068910c65cfedf3f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-94774d9285169be4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SharedTaskSetsPage.jsx:108"}, "properties": {"repobilityId": "b25c0beb623b2115", "scanner": "scanner-primary", "fingerprint": "94774d9285169be4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-661511d78b7f53b3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTicketsPage.jsx:87"}, "properties": {"repobilityId": "08163a2f62cf1aae", "scanner": "scanner-primary", "fingerprint": "661511d78b7f53b3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c8a5ebb43bd532cc", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AppsPage.jsx:40"}, "properties": {"repobilityId": "586c52be085c5445", "scanner": "scanner-primary", "fingerprint": "c8a5ebb43bd532cc", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-608e2859a84ffb59", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/KidTasksPage.jsx:272"}, "properties": {"repobilityId": "cd7fed31eef3f136", "scanner": "scanner-primary", "fingerprint": "608e2859a84ffb59", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-fb993fcdb4857d9b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsUserDetailPage.jsx:346"}, "properties": {"repobilityId": "85558ab363d980c0", "scanner": "scanner-primary", "fingerprint": "fb993fcdb4857d9b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f95de3f74652c675", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/UserTaskDetailPage.jsx:1162"}, "properties": {"repobilityId": "03b0108b6cfd1f97", "scanner": "scanner-primary", "fingerprint": "f95de3f74652c675", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b1338fb12b4a5f6b", "level": "none", "message": {"text": "TODO/FIXME marker in shipping code \u2014 client/src/pages/UserTaskDetailPage.jsx:1013"}, "properties": {"repobilityId": "953786d021a915ae", "scanner": "scanner-primary", "fingerprint": "b1338fb12b4a5f6b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.todo-marker"]}}, {"ruleId": "scanner-47516f5dd9cda88f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsBadgesPage.jsx:220"}, "properties": {"repobilityId": "6cfb5944043bf5e6", "scanner": "scanner-primary", "fingerprint": "47516f5dd9cda88f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0d1cf1785cd685a3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/AdminPage.jsx:259"}, "properties": {"repobilityId": "f9f141c3789ce662", "scanner": "scanner-primary", "fingerprint": "0d1cf1785cd685a3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-23940af0e4242299", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/InboxKidPage.jsx:140"}, "properties": {"repobilityId": "06f33da8b1e897e8", "scanner": "scanner-primary", "fingerprint": "23940af0e4242299", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5e1ecb23cc0b7e7c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 client/src/pages/SettingsTasksPage.jsx:492"}, "properties": {"repobilityId": "55a87694e6de04cc", "scanner": "scanner-primary", "fingerprint": "5e1ecb23cc0b7e7c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3a5b35f6a5917300", "level": "none", "message": {"text": "TODO/FIXME marker in shipping code \u2014 client/dev-dist/workbox-5a5d9309.js:787"}, "properties": {"repobilityId": "748df17fb6ae8a39", "scanner": "scanner-primary", "fingerprint": "3a5b35f6a5917300", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.todo-marker"]}}, {"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-835eae4c7f17a2f8", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:22-alpine"}, "properties": {"repobilityId": "e469cde27591e1b7", "scanner": "scanner-primary", "fingerprint": "835eae4c7f17a2f8", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-835eae4c7f17a2f8", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:22-alpine"}, "properties": {"repobilityId": "3aa9fc54414fe4e2", "scanner": "scanner-primary", "fingerprint": "835eae4c7f17a2f8", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 14}}}]}, {"ruleId": "scanner-2c292b667c5cde44", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:22-slim"}, "properties": {"repobilityId": "08300d3e0ca44a69", "scanner": "scanner-primary", "fingerprint": "2c292b667c5cde44", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/claude-code/Dockerfile"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-7a17b21c3e5625a4", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in server/scripts/importAwards.js:17"}, "properties": {"repobilityId": "c1a5c18480989e7e", "scanner": "scanner-primary", "fingerprint": "7a17b21c3e5625a4", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/scripts/importAwards.js"}, "region": {"startLine": 17}}}]}, {"ruleId": "scanner-01ec91fa74643b93", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in server/scripts/cropBadgeImages.js:28"}, "properties": {"repobilityId": "f468b7712e46b5b5", "scanner": "scanner-primary", "fingerprint": "01ec91fa74643b93", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/scripts/cropBadgeImages.js"}, "region": {"startLine": 28}}}]}, {"ruleId": "scanner-2ef0ffbbbd54a888", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:37"}, "properties": {"repobilityId": "a4c9cb03d6dd929f", "scanner": "scanner-primary", "fingerprint": "2ef0ffbbbd54a888", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/public.stale-2026-05-20/assets/index-6w5DCHJE.js"}, "region": {"startLine": 37}}}]}, {"ruleId": "scanner-cfa2bc4f5e73d4e0", "level": "warning", "message": {"text": "Insecure pattern 'direct_outerhtml_assignment' in server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:633"}, "properties": {"repobilityId": "6e7c8f04aa4696a0", "scanner": "scanner-primary", "fingerprint": "cfa2bc4f5e73d4e0", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_outerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/public.stale-2026-05-20/assets/index-6w5DCHJE.js"}, "region": {"startLine": 633}}}]}, {"ruleId": "scanner-d06a0ef3178bac48", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in server/src/app.js:151"}, "properties": {"repobilityId": "c938f5eb15c77374", "scanner": "scanner-primary", "fingerprint": "d06a0ef3178bac48", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/src/app.js"}, "region": {"startLine": 151}}}]}, {"ruleId": "scanner-9c6ce957ecdec0ec", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in server/src/sdk/multiplayer.js:496"}, "properties": {"repobilityId": "a12e0090aa18e06d", "scanner": "scanner-primary", "fingerprint": "9c6ce957ecdec0ec", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "server/src/sdk/multiplayer.js"}, "region": {"startLine": 496}}}]}, {"ruleId": "scanner-d805794d1712edcf", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in client/src/components/claude/KidWorkspace.jsx:89"}, "properties": {"repobilityId": "b8325d6d2292202a", "scanner": "scanner-primary", "fingerprint": "d805794d1712edcf", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "client/src/components/claude/KidWorkspace.jsx"}, "region": {"startLine": 89}}}]}, {"ruleId": "scanner-3d9e5fdaa2c4363a", "level": "note", "message": {"text": "Very large file: server/src/routes/userTasks.js (1631 lines)"}, "properties": {"repobilityId": "5dd2b98cc70ee628", "scanner": "scanner-primary", "fingerprint": "3d9e5fdaa2c4363a", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-a56eced344632307", "level": "note", "message": {"text": "Very large file: client/src/components/shared/Layout.jsx (1291 lines)"}, "properties": {"repobilityId": "5f4b4c458570f0a3", "scanner": "scanner-primary", "fingerprint": "a56eced344632307", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-49e1efd94d9e1f19", "level": "note", "message": {"text": "Very large file: client/src/components/bank/MoneyPopover.jsx (1750 lines)"}, "properties": {"repobilityId": "8ea9c2e1ddc8edff", "scanner": "scanner-primary", "fingerprint": "49e1efd94d9e1f19", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-bacf69e8e4d768c0", "level": "note", "message": {"text": "Very large file: client/src/pages/TaskSetDetailPage.jsx (1126 lines)"}, "properties": {"repobilityId": "b88108cbdf2529ea", "scanner": "scanner-primary", "fingerprint": "bacf69e8e4d768c0", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-ae59b3257cf18cc5", "level": "note", "message": {"text": "Very large file: client/src/pages/UserTaskDetailPage.jsx (3577 lines)"}, "properties": {"repobilityId": "a66914aa30a5c143", "scanner": "scanner-primary", "fingerprint": "ae59b3257cf18cc5", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-60f845579f845d72", "level": "note", "message": {"text": "Very large file: client/dev-dist/workbox-5a5d9309.js (3395 lines)"}, "properties": {"repobilityId": "6f8011755440f493", "scanner": "scanner-primary", "fingerprint": "60f845579f845d72", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-6893a6c8b0861585", "level": "warning", "message": {"text": "Very low test-to-source ratio"}, "properties": {"repobilityId": "54a7de3f06314bf0", "scanner": "scanner-primary", "fingerprint": "6893a6c8b0861585", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["tests", "coverage"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "5733592ae1092c71", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "e4d1b28af684dfa1", "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": "189cedc8c172c991", "scanner": "scanner-primary", "fingerprint": "72b2a6250083a784", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "incomplete", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "dd2a5d60cd804d8e", "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": "note", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "9a0116aa5d8ded46", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "low", "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": "657c2a52c0649a57", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-bea357a6497a2d5d", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: CLAUDE.md"}, "properties": {"repobilityId": "aae72df3934829ac", "scanner": "scanner-primary", "fingerprint": "bea357a6497a2d5d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "CLAUDE.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-2c5f98b152cddb6d", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: .claude/launch.json"}, "properties": {"repobilityId": "0de48c0f4ab303d8", "scanner": "scanner-primary", "fingerprint": "2c5f98b152cddb6d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".claude/launch.json"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-c0731726dac3029c", "level": "none", "message": {"text": "Commented-code block (7 lines) in server/scripts/scrapeCuBadges.js:161"}, "properties": {"repobilityId": "801ea438ce2e5903", "scanner": "scanner-primary", "fingerprint": "c0731726dac3029c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-96454ce12b0f2af3", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/scrapeCuBadges.js:80"}, "properties": {"repobilityId": "09d76fbd0a3013d6", "scanner": "scanner-primary", "fingerprint": "96454ce12b0f2af3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-7000eebfe3c4f9c3", "level": "none", "message": {"text": "Commented-code block (6 lines) in server/scripts/parseScrapedTexts.js:80"}, "properties": {"repobilityId": "7b3326070165e12b", "scanner": "scanner-primary", "fingerprint": "7000eebfe3c4f9c3", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-2315a7b51fd0856b", "level": "none", "message": {"text": "Commented-code block (6 lines) in server/scripts/importBadgeLibrary.js:94"}, "properties": {"repobilityId": "6a9bc606de76ff6a", "scanner": "scanner-primary", "fingerprint": "2315a7b51fd0856b", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-16c2b0d8f2c19cfa", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/auditBadgeSitemap.js:32"}, "properties": {"repobilityId": "1742377efaa2ac54", "scanner": "scanner-primary", "fingerprint": "16c2b0d8f2c19cfa", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-91d36b7c59888a77", "level": "none", "message": {"text": "Commented-code block (8 lines) in server/scripts/importAwards.js:28"}, "properties": {"repobilityId": "4b9f3c239849acca", "scanner": "scanner-primary", "fingerprint": "91d36b7c59888a77", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-4232c53b82a5692f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/scripts/importAwards.js:509"}, "properties": {"repobilityId": "adc1dc28f3f147bc", "scanner": "scanner-primary", "fingerprint": "4232c53b82a5692f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3ea3a61c4e29f31e", "level": "none", "message": {"text": "Commented-code block (6 lines) in server/scripts/fixMisattributedBadges.js:76"}, "properties": {"repobilityId": "2eeb79d425d6f08e", "scanner": "scanner-primary", "fingerprint": "3ea3a61c4e29f31e", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-5390136793d6bc6b", "level": "none", "message": {"text": "Commented-code block (10 lines) in server/scripts/importBadges.js:51"}, "properties": {"repobilityId": "8a810375324df906", "scanner": "scanner-primary", "fingerprint": "5390136793d6bc6b", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-a5592694b66dd135", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/workbox-8c29f6e4.js:1"}, "properties": {"repobilityId": "c06e7b5bd47688ee", "scanner": "scanner-primary", "fingerprint": "a5592694b66dd135", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-9fc2aa906a67d9d9", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/public.stale-2026-05-20/assets/index-6w5DCHJE.js:1"}, "properties": {"repobilityId": "749bf08dda0d4c2a", "scanner": "scanner-primary", "fingerprint": "9fc2aa906a67d9d9", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-5ae2e76fa0b66bb1", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/app.js:144"}, "properties": {"repobilityId": "2344f2a0d0c5c23f", "scanner": "scanner-primary", "fingerprint": "5ae2e76fa0b66bb1", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-e4ed52cb964a08cd", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/db/migrations.js:401"}, "properties": {"repobilityId": "2f8bd6c573d225e9", "scanner": "scanner-primary", "fingerprint": "e4ed52cb964a08cd", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-ad5724c920014a8e", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/services/dockerService.js:162"}, "properties": {"repobilityId": "71390be73f54ed28", "scanner": "scanner-primary", "fingerprint": "ad5724c920014a8e", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f52b064ef4855f2c", "level": "none", "message": {"text": "Commented-code block (6 lines) in server/src/services/awardSync.js:41"}, "properties": {"repobilityId": "f450819824e74e54", "scanner": "scanner-primary", "fingerprint": "f52b064ef4855f2c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-9ad19f83fb98b867", "level": "none", "message": {"text": "Commented-code block (7 lines) in server/src/services/wsService.js:8"}, "properties": {"repobilityId": "6e9fc0bc5af0cfef", "scanner": "scanner-primary", "fingerprint": "9ad19f83fb98b867", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f927bffa4325221a", "level": "none", "message": {"text": "Commented-code block (8 lines) in server/src/routes/badges.js:107"}, "properties": {"repobilityId": "6277e50992ae369a", "scanner": "scanner-primary", "fingerprint": "f927bffa4325221a", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-ee7f99064e2967c9", "level": "none", "message": {"text": "Commented-code block (7 lines) in server/src/routes/claude.js:77"}, "properties": {"repobilityId": "2516911f5938e3d8", "scanner": "scanner-primary", "fingerprint": "ee7f99064e2967c9", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c00c3238581bf7fb", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/activity.js:71"}, "properties": {"repobilityId": "a427b5bcabefdd1b", "scanner": "scanner-primary", "fingerprint": "c00c3238581bf7fb", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-cfc000aa77e6b8a6", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/family.js:66"}, "properties": {"repobilityId": "4d2e39b924388eb7", "scanner": "scanner-primary", "fingerprint": "cfc000aa77e6b8a6", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-791ebcf766407306", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/userTasks.js:112"}, "properties": {"repobilityId": "b8a24b7325c3be97", "scanner": "scanner-primary", "fingerprint": "791ebcf766407306", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-30e5ccd77883f309", "level": "none", "message": {"text": "Commented-code block (6 lines) in server/src/routes/auth.js:27"}, "properties": {"repobilityId": "709df5c9c7abd633", "scanner": "scanner-primary", "fingerprint": "30e5ccd77883f309", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c8bcc6cbbb5cc506", "level": "none", "message": {"text": "Commented-code block (6 lines) in client/src/debug/eventLog.js:1"}, "properties": {"repobilityId": "31b5559235bb5dce", "scanner": "scanner-primary", "fingerprint": "c8bcc6cbbb5cc506", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-3f4640c4c5508ba3", "level": "none", "message": {"text": "Commented-code block (10 lines) in client/src/utils/appHeight.js:4"}, "properties": {"repobilityId": "b92f1a15e5464df3", "scanner": "scanner-primary", "fingerprint": "3f4640c4c5508ba3", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-6a4a6e7ed6cd28a8", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/utils/earnBadgeRef.jsx:1"}, "properties": {"repobilityId": "5c367aa4e22ca426", "scanner": "scanner-primary", "fingerprint": "6a4a6e7ed6cd28a8", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c7649990ef0d82c4", "level": "none", "message": {"text": "Commented-code block (6 lines) in client/src/utils/awardProgress.js:1"}, "properties": {"repobilityId": "cf95c6ab14c5684b", "scanner": "scanner-primary", "fingerprint": "c7649990ef0d82c4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-0cb6923f268923af", "level": "none", "message": {"text": "Commented-code block (8 lines) in client/src/hooks/useOverscrollGuard.js:3"}, "properties": {"repobilityId": "bbc41853b9a1fee3", "scanner": "scanner-primary", "fingerprint": "0cb6923f268923af", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-4e6370dba187e0dc", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/hooks/useScrollLock.js:31"}, "properties": {"repobilityId": "6478ae3770c7de2e", "scanner": "scanner-primary", "fingerprint": "4e6370dba187e0dc", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-dfc4470c5f1f582a", "level": "none", "message": {"text": "Commented-code block (7 lines) in client/src/context/AuthContext.jsx:21"}, "properties": {"repobilityId": "abbe99da224a806a", "scanner": "scanner-primary", "fingerprint": "dfc4470c5f1f582a", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-b927a04baeb2c1ad", "level": "none", "message": {"text": "Commented-code block (8 lines) in client/src/components/badges/BadgeBrowser.jsx:192"}, "properties": {"repobilityId": "fa0f143fd383af56", "scanner": "scanner-primary", "fingerprint": "b927a04baeb2c1ad", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-156f349ad7c1ba08", "level": "none", "message": {"text": "Commented-code block (11 lines) in client/src/components/tasks/TaskSetCard.jsx:119"}, "properties": {"repobilityId": "0f3a3026a7f0cf37", "scanner": "scanner-primary", "fingerprint": "156f349ad7c1ba08", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-adeb32764b0f194c", "level": "none", "message": {"text": "Commented-code block (7 lines) in client/src/components/shared/Layout.jsx:103"}, "properties": {"repobilityId": "04f4a9c44c7de911", "scanner": "scanner-primary", "fingerprint": "adeb32764b0f194c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-18454211bdcf17af", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/components/shared/DebugOverlay.jsx:4"}, "properties": {"repobilityId": "2a88fe23411fe3c2", "scanner": "scanner-primary", "fingerprint": "18454211bdcf17af", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-2d6a6ca61db2ff0f", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/components/awards/CountAtLevelAwardDetail.jsx:111"}, "properties": {"repobilityId": "6d02ece1b4b1d2c7", "scanner": "scanner-primary", "fingerprint": "2d6a6ca61db2ff0f", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f78a2f3ffd9846d3", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/components/bank/MoneyPopover.jsx:473"}, "properties": {"repobilityId": "0e13135a4d746190", "scanner": "scanner-primary", "fingerprint": "f78a2f3ffd9846d3", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-97c8d688db797dae", "level": "none", "message": {"text": "Commented-code block (5 lines) in client/src/pages/KidGroupPage.jsx:106"}, "properties": {"repobilityId": "b6c39697f37749ad", "scanner": "scanner-primary", "fingerprint": "97c8d688db797dae", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-1a1929b292a2450b", "level": "none", "message": {"text": "Commented-code block (9 lines) in client/src/pages/AwardTreePage.jsx:183"}, "properties": {"repobilityId": "9e32795bf114a407", "scanner": "scanner-primary", "fingerprint": "1a1929b292a2450b", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-e3bdf53020a7c978", "level": "none", "message": {"text": "Commented-code block (6 lines) in client/src/pages/KidTasksPage.jsx:93"}, "properties": {"repobilityId": "4c3d8717901667ac", "scanner": "scanner-primary", "fingerprint": "e3bdf53020a7c978", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-6d35c475a5b9c014", "level": "none", "message": {"text": "Commented-code block (6 lines) in client/src/pages/UserTaskDetailPage.jsx:77"}, "properties": {"repobilityId": "a5157487257a04ed", "scanner": "scanner-primary", "fingerprint": "6d35c475a5b9c014", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-03d4b2a83432a291", "level": "none", "message": {"text": "Commented-code block (11 lines) in client/src/constants/badgeLevels.js:1"}, "properties": {"repobilityId": "f3499c4b32f6e1ec", "scanner": "scanner-primary", "fingerprint": "03d4b2a83432a291", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-74647f8faca9d513", "level": "none", "message": {"text": "Commented-code block (7 lines) in client/dev-dist/workbox-5a5d9309.js:2535"}, "properties": {"repobilityId": "6fa7db1a10f98168", "scanner": "scanner-primary", "fingerprint": "74647f8faca9d513", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c6346559dab44c21", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 client/dev-dist/workbox-5a5d9309.js:750"}, "properties": {"repobilityId": "7530f9dee1ebfd78", "scanner": "scanner-primary", "fingerprint": "c6346559dab44c21", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f808a4ef3e1071fb", "level": "note", "message": {"text": "14 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "ce6b2ec4be4ec75a", "scanner": "scanner-primary", "fingerprint": "f808a4ef3e1071fb", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-38633a2cc8c40293", "level": "warning", "message": {"text": "Frontend route `/terminal/:userId` has no Link/navigate to it \u2014 client/src/App.jsx"}, "properties": {"repobilityId": "4fed4545657ad7f5", "scanner": "scanner-primary", "fingerprint": "38633a2cc8c40293", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-c5460862000b0fb3", "level": "warning", "message": {"text": "Frontend route `/tasks/:userId/group/:groupKey` has no Link/navigate to it \u2014 client/src/App.jsx"}, "properties": {"repobilityId": "e263392c53890244", "scanner": "scanner-primary", "fingerprint": "c5460862000b0fb3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-426c37d6e72db282", "level": "warning", "message": {"text": "Frontend route `/tasks/:userId/:taskSetId/tree` has no Link/navigate to it \u2014 client/src/App.jsx"}, "properties": {"repobilityId": "835cadaf2763a650", "scanner": "scanner-primary", "fingerprint": "426c37d6e72db282", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-9cca16177040dbd5", "level": "warning", "message": {"text": "Frontend route `/badges/:userId` has no Link/navigate to it \u2014 client/src/App.jsx"}, "properties": {"repobilityId": "450e1daa4fa800cc", "scanner": "scanner-primary", "fingerprint": "9cca16177040dbd5", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-f92eb29f314390c3", "level": "warning", "message": {"text": "Frontend route `/settings/turns/:id` has no Link/navigate to it \u2014 client/src/App.jsx"}, "properties": {"repobilityId": "2024e8e07fee17c7", "scanner": "scanner-primary", "fingerprint": "f92eb29f314390c3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}]}]}