{"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-a3725ae325aca13d", "name": "Possibly dead Python function: run_seeds", "shortDescription": {"text": "Possibly dead Python function: run_seeds"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d45e5050fc15bf1e", "name": "Possibly dead Python function: cleanup_shares", "shortDescription": {"text": "Possibly dead Python function: cleanup_shares"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f8da5d8917c4c097", "name": "Possibly dead Python function: jaccard", "shortDescription": {"text": "Possibly dead Python function: jaccard"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-af52997b5f40622b", "name": "Possibly dead Python function: sync_all_users_background", "shortDescription": {"text": "Possibly dead Python function: sync_all_users_background"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-97be83e3d05677c8", "name": "Possibly dead Python function: archive_prev_semester_background", "shortDescription": {"text": "Possibly dead Python function: archive_prev_semester_background"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7058450ee2f43a54", "name": "Possibly dead Python function: generate_student_pdf", "shortDescription": {"text": "Possibly dead Python function: generate_student_pdf"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-a5273fcc857fa038", "name": "Possibly dead Python function: resolve_grader_model", "shortDescription": {"text": "Possibly dead Python function: resolve_grader_model"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0a654e56d3f5ac61", "name": "Possibly dead Python function: grade_pending_for_student", "shortDescription": {"text": "Possibly dead Python function: grade_pending_for_student"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b552fc92bcf0dab7", "name": "Possibly dead Python function: grade_problem_set", "shortDescription": {"text": "Possibly dead Python function: grade_problem_set"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-292ee5340181940a", "name": "Possibly dead Python function: restart_services", "shortDescription": {"text": "Possibly dead Python function: restart_services"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-99f94c1e9416db3b", "name": "Possibly dead Python function: start_scheduler", "shortDescription": {"text": "Possibly dead Python function: start_scheduler"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-db3fb1492380b51a", "name": "Possibly dead Python function: start_scheduler", "shortDescription": {"text": "Possibly dead Python function: start_scheduler"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-06d916fb58e2eece", "name": "Possibly dead Python function: require_role", "shortDescription": {"text": "Possibly dead Python function: require_role"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f02306b786f85fca", "name": "Possibly dead Python function: safe_storage_filename", "shortDescription": {"text": "Possibly dead Python function: safe_storage_filename"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-120c8532c0ad70c0", "name": "Possibly dead Python function: verify_csrf", "shortDescription": {"text": "Possibly dead Python function: verify_csrf"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9baf38c3740b6a3d", "name": "Possibly dead Python function: is_feature_enabled", "shortDescription": {"text": "Possibly dead Python function: is_feature_enabled"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3c0c862d7fd26729", "name": "Possibly dead Python function: include_object", "shortDescription": {"text": "Possibly dead Python function: include_object"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d1abbd7a8c8f523e", "name": "Stray `console.log` in TS/JS \u2014 frontend/scripts/copy-standalone.js:15", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 frontend/scripts/copy-standalone.js:15"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6e6855b3f7642d6e", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/StudentPickerModal.tsx:253", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/StudentPickerModal.tsx:253"}, "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-147df8e37526f6a7", "name": "Icon-only button without accessible name \u2014 frontend/src/components/timetable/MyEventsModal.tsx:149", "shortDescription": {"text": "Icon-only button without accessible name \u2014 frontend/src/components/timetable/MyEventsModal.tsx:149"}, "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-f8f2bc1941ea105a", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/timetable/MyEventsModal.tsx:238", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/timetable/MyEventsModal.tsx:238"}, "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-b694b08e51225455", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/courseware/ProblemContent.tsx:157", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/courseware/ProblemContent.tsx:157"}, "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-11a0279626fd7aa6", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/courseware/MyCoursewareView.tsx:434", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/courseware/MyCoursewareView.tsx:434"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-4ad862e95cbd5f02", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/PresentMode.tsx:121", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/PresentMode.tsx:121"}, "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-4dfe91c309f64d50", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/DeckEditor.tsx:223", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/DeckEditor.tsx:223"}, "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-a44e25069aa5a708", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/ThemePicker.tsx:94", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/ThemePicker.tsx:94"}, "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-2e5c1f7f0a2cd842", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/docs/SlashCommand.tsx:212", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/docs/SlashCommand.tsx:212"}, "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-90a08e6ccc888021", "name": "\"active\" state uses light bg in a dark theme \u2014 frontend/src/components/docs/SlashCommand.tsx:207", "shortDescription": {"text": "\"active\" state uses light bg in a dark theme \u2014 frontend/src/components/docs/SlashCommand.tsx:207"}, "fullDescription": {"text": "A ternary like `active ? 'bg-white' : '...'` (or bg-gray-100/200) on a dark theme produces jarring white pills. Use a dark-emphasized active state instead \u2014 border + ring or slightly brighter dark bg. Example: `active ? 'bg-gray-800 border-gray-500 ring-1 ring-blue-500/30' : '\u2026'`.\n\nWhy: P-E in CHECKLIST.md \u2014 light bg in a dark theme is a class of regression.\nRule id: fq.active-light-bg"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-04cf0a8c41a5ab70", "name": "Stray `console.log` in TS/JS \u2014 frontend/src/components/docs/CollabEditor.tsx:98", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 frontend/src/components/docs/CollabEditor.tsx:98"}, "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-f648baa5a6024261", "name": "Stray `console.log` in TS/JS \u2014 frontend/src/components/ui/Toast.tsx:43", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 frontend/src/components/ui/Toast.tsx: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-cd7125b6d226bea2", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/workspace/MyWorkspaceView.tsx:206", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/workspace/MyWorkspaceView.tsx:206"}, "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-b6e63a2786857802", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/MoveToFolderModal.tsx:118", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/MoveToFolderModal.tsx:118"}, "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-99f8134cad07b3e9", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/ItemCard.tsx:102", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/ItemCard.tsx:102"}, "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-d581dee0e61a3f72", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/DrivePage.tsx:544", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/DrivePage.tsx:544"}, "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-1ff626e20ee76f4a", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderSidebar.tsx:201", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderSidebar.tsx:201"}, "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-4dc9b9548c4fd535", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderCard.tsx:66", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderCard.tsx:66"}, "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-e1e86815d09a52dd", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/GoogleDriveSidePanel.tsx:178", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/GoogleDriveSidePanel.tsx:178"}, "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-50b3e1b531e9c34f", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/chat/SessionGroup.tsx:89", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/chat/SessionGroup.tsx:89"}, "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-218584610fc4ee7f", "name": "Icon-only button without accessible name \u2014 frontend/src/components/admin/ClubAssignmentModal.tsx:70", "shortDescription": {"text": "Icon-only button without accessible name \u2014 frontend/src/components/admin/ClubAssignmentModal.tsx:70"}, "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-67b8b3d36158b0ff", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/sidebar.tsx:231", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/sidebar.tsx: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-c4238d30c822fdab", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/UserSearchInput.tsx:148", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/UserSearchInput.tsx:148"}, "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-5867898dd7faa5c5", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/EnrollmentPositionsModal.tsx:170", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/EnrollmentPositionsModal.tsx:170"}, "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-c2334ee614b48dae", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/whiteboard/WhiteboardCanvas.tsx:527", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/whiteboard/WhiteboardCanvas.tsx:527"}, "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-80e2f0cc1f7d2940", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/wordbook/StudyView.tsx:273", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/wordbook/StudyView.tsx:273"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-e00f2ce2ec0a4926", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostDetailView.tsx:435", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostDetailView.tsx:435"}, "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-68e88d2fca237c41", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/DrivePicker.tsx:171", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/DrivePicker.tsx:171"}, "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-9c585ae6b1fef215", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/UserPicker.tsx:266", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/UserPicker.tsx:266"}, "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-121e97a85f89b3a7", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ReusePostModal.tsx:140", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ReusePostModal.tsx: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-7f0d27e24a084758", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostStreamCard.tsx:97", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostStreamCard.tsx:97"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-da13ccb5caf541ce", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/GoogleDocPreview.tsx:77", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/GoogleDocPreview.tsx:77"}, "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-979cf7326f593371", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/SubmissionPanel.tsx:298", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/SubmissionPanel.tsx:298"}, "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-a5657a65ef4127be", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ChatbotPickerModal.tsx:92", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ChatbotPickerModal.tsx:92"}, "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-abe1efeddf101fb0", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseChatbots.tsx:149", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseChatbots.tsx:149"}, "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-26126f3355d50980", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/BoardPickerModal.tsx:115", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/BoardPickerModal.tsx: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-6dd38715efaccd23", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareLinkModal.tsx:128", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareLinkModal.tsx: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-62716ad3d2130ff1", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/QuizPickerModal.tsx:99", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/QuizPickerModal.tsx:99"}, "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-398b671380a37830", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WordDeckPickerModal.tsx:115", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WordDeckPickerModal.tsx: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-f8e1f98bc48c1dc8", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseInfoWidget.tsx:76", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseInfoWidget.tsx:76"}, "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-18a128c4409bfdbd", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WhiteboardPickerModal.tsx:111", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WhiteboardPickerModal.tsx:111"}, "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-d240b60ddd7feede", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareDocModal.tsx:185", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareDocModal.tsx:185"}, "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-58fed7699e99726d", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/AssignmentModal.tsx:617", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/AssignmentModal.tsx:617"}, "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-19a2944906c5ec32", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/tools/ToolShareModal.tsx:72", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/tools/ToolShareModal.tsx:72"}, "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-9cbd3aacf2cdbae2", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/board/BoardView.tsx:253", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/board/BoardView.tsx:253"}, "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-ce0d7c841da809db", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/layout.tsx:84", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/layout.tsx:84"}, "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-34dad86985a2c319", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/page.tsx:126", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/page.tsx:126"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f63755bf2346df6c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/[id]/page.tsx:608", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/[id]/page.tsx:608"}, "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-b1a9b901859ec599", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/admissions/analysis/page.tsx:84", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/admissions/analysis/page.tsx:84"}, "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-990b82753405d113", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/dashboard/page.tsx:282", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/dashboard/page.tsx:282"}, "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-a0ed6994c05cff91", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/_tabs/ArtifactsTab.tsx:63", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/_tabs/ArtifactsTab.tsx:63"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f8090ba8b1806ed6", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/artifacts-gallery/page.tsx:107", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/artifacts-gallery/page.tsx:107"}, "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-b40c0f5b03db8ae3", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/announcements/page.tsx:253", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/announcements/page.tsx:253"}, "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-3eca99ac5a43dd99", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/past-research/page.tsx:457", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/past-research/page.tsx:457"}, "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-01dd9fc9b46c4e5b", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/departments/[id]/delegations/page.t", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/departments/[id]/delegations/page.tsx:158"}, "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-5251b6bd09855c5c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/backup/_components/BackupSchedule.t", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/backup/_components/BackupSchedule.tsx:301"}, "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-2718a5eeaf47a28f", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/ai-developer/page.tsx:230", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/ai-developer/page.tsx:230"}, "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-c3eda40738e9f189", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormM", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormModal.tsx:410"}, "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-7bb226039f88ecf7", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/courseware/[psid]/page.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/courseware/[psid]/page.tsx:116"}, "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-c2df1ee6cc30b312", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/decks/page.tsx:144", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/decks/page.tsx:144"}, "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-eecde1ec269c8936", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/docs/page.tsx:159", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/docs/page.tsx:159"}, "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-d7243d941b5c9bc9", "name": "Icon-only button without accessible name \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:201", "shortDescription": {"text": "Icon-only button without accessible name \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:201"}, "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-194ff1c873ad6192", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:147", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:147"}, "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-6e8e60e185bfc0d6", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/_components/Coursework.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/_components/Coursework.tsx:264"}, "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-b5c8e3a925afcb5c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PermissionGroupsTab.tsx:", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PermissionGroupsTab.tsx:338"}, "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-36e6622c620c6768", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PositionTemplatesTab.tsx", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PositionTemplatesTab.tsx:375"}, "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-c7ab47aa00ed520b", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/page.tsx:127", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/page.tsx:127"}, "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-bfe73052260b5774", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/[sid]/host/page.tsx:177", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/[sid]/host/page.tsx:177"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-997e49afdea12f06", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/whiteboard/page.tsx:76", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/whiteboard/page.tsx:76"}, "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-d9148246a454e996", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/page.tsx:92", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/page.tsx:92"}, "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-d24d3492fb7cca15", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/[did]/page.tsx:356", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/[did]/page.tsx:356"}, "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-21716d8397908df1", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/page.tsx:161", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/page.tsx:161"}, "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-71e6a7bba7f32d42", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/[sid]/host/page.tsx:124", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/[sid]/host/page.tsx: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-48a3914dbbdff79c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/board/page.tsx:70", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/board/page.tsx:70"}, "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-d8255a617243d6aa", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/papers/feed/page.tsx:158", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/papers/feed/page.tsx:158"}, "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-90a87600b3f32e36", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/sheets/[sid]/page.tsx:79", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/sheets/[sid]/page.tsx:79"}, "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-e57c139b8ec46908", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/decks/[did]/page.tsx:77", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/decks/[did]/page.tsx:77"}, "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-151310a0ad54240c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/docs/[did]/page.tsx:65", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/docs/[did]/page.tsx:65"}, "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-e09b7c3e15732a3f", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/hwps/[hid]/page.tsx:65", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/hwps/[hid]/page.tsx:65"}, "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-d19e79a634d0fa89", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/layout.tsx:71", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/layout.tsx:71"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-1d058a389d5eb9a8", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-activities/page.tsx:143", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-activities/page.tsx:143"}, "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-f2e4dc5f660970a0", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/docs/page.tsx:157", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/docs/page.tsx:157"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f2ade85a0392ada0", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/dashboard/page.tsx:105", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/dashboard/page.tsx:105"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-6e27722bceec3a78", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/announcements/page.tsx:77", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/announcements/page.tsx:77"}, "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-48367bd5a4dccfa1", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/quiz/[pin]/page.tsx:179", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/quiz/[pin]/page.tsx:179"}, "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-c3619f02b801767f", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/past-research/page.tsx:232", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/past-research/page.tsx:232"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-9facd18fa9df5099", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.tsx:251"}, "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-ff42d1b9bf175e9c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/TimelineTab.t", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/TimelineTab.tsx:81"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-da850d3e6b1ff066", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/wordbook/page.tsx:59", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/wordbook/page.tsx:59"}, "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-ab929d8d0148ea28", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/decks/page.tsx:87", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/decks/page.tsx: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-d55e704fa2a8bc48", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/docs/page.tsx:94", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/docs/page.tsx:94"}, "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-33de087ae76b7b17", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/surveys/page.tsx:95", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/surveys/page.tsx:95"}, "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-abdfe796212758f3", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/_components/StudentCou", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/_components/StudentCourseworkList.tsx:162"}, "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-4b8cb5603325086b", "name": "Stray `console.log` in TS/JS \u2014 backend-hocuspocus/src/server.ts:99", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 backend-hocuspocus/src/server.ts:99"}, "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-f9f4cbbfa0125b40", "name": "Privileged port 587 in use", "shortDescription": {"text": "Privileged port 587 in use"}, "fullDescription": {"text": "Port 587 is privileged (<1024). Make sure the service runs with the right caps or front it with a non-privileged port via a load balancer."}, "properties": {"scanner": "scanner-primary", "layer": "network", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2fbc1d949b7861fd", "name": "Privileged port 465 in use", "shortDescription": {"text": "Privileged port 465 in use"}, "fullDescription": {"text": "Port 465 is privileged (<1024). Make sure the service runs with the right caps or front it with a non-privileged port via a load balancer."}, "properties": {"scanner": "scanner-primary", "layer": "network", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-aa5acaa49eb8315b", "name": "Containers defined but no K8s/orchestration manifest found", "shortDescription": {"text": "Containers defined but no K8s/orchestration manifest found"}, "fullDescription": {"text": "Repo has Dockerfiles/compose but no Kubernetes/Nomad manifests. If the target deployment is K8s, the manifests may live in a separate ops repo."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1094a5c7ee42a291", "name": "Insecure pattern 'direct_innerhtml_assignment' in frontend/src/components/hwp/HwpEditor.tsx:131", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in frontend/src/components/hwp/HwpEditor.tsx:131"}, "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-1d474224d13bce39", "name": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/courseware/ProblemContent.tsx:157", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/courseware/ProblemContent.tsx:157"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8b7aac71ba86c427", "name": "Insecure pattern 'exec_used' in frontend/src/components/docs/EditorContextMenu.tsx:138", "shortDescription": {"text": "Insecure pattern 'exec_used' in frontend/src/components/docs/EditorContextMenu.tsx:138"}, "fullDescription": {"text": "Found a known-risky pattern (exec_used). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-c74e85073d4bd40f", "name": "Insecure pattern 'local_storage_auth_token' in frontend/src/app/auth/register/page.tsx:62", "shortDescription": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/app/auth/register/page.tsx:62"}, "fullDescription": {"text": "Found a known-risky pattern (local_storage_auth_token). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-633c813bde13fe1f", "name": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/auth-context.tsx:146", "shortDescription": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/auth-context.tsx:146"}, "fullDescription": {"text": "Found a known-risky pattern (local_storage_auth_token). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-907ff164d2379a68", "name": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/api/client.ts:29", "shortDescription": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/api/client.ts:29"}, "fullDescription": {"text": "Found a known-risky pattern (local_storage_auth_token). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a65ef6dd39dfecee", "name": "Very large file: frontend/src/components/board/BoardView.tsx (1756 lines)", "shortDescription": {"text": "Very large file: frontend/src/components/board/BoardView.tsx (1756 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-510b8a489f0dbc29", "name": "Very large file: frontend/src/app/(admin)/record-writer/[id]/page.tsx (1392 lines)", "shortDescription": {"text": "Very large file: frontend/src/app/(admin)/record-writer/[id]/page.tsx (1392 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-f8de88d795196173", "name": "Very large file: backend/tests/test_edutools.py (1211 lines)", "shortDescription": {"text": "Very large file: backend/tests/test_edutools.py (1211 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": "53 test file(s) for 798 source file(s) (ratio 0.07). 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 109 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 359 placeholder/mock markers across 135 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-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. Treat this as a contract gap: the agent is guided, but the generated output is not yet guarded by the controls that make it repeatable."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ea8f3013f588db25", "name": "Shallow git history limits provenance confidence", "shortDescription": {"text": "Shallow git history limits provenance confidence"}, "fullDescription": {"text": "The repository is a shallow clone. Origin/evolution analysis cannot distinguish fresh generation, imported legacy code, or long-lived human code with high confidence."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8424db9c75e04ba4", "name": "Very short observed git history", "shortDescription": {"text": "Very short observed git history"}, "fullDescription": {"text": "The repo has multiple source files but two or fewer visible commits. This is not a failure by itself, but it lowers confidence in evolution-based diagnosis."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-17d866d91a7c451e", "name": "Agent instruction/config may expose a secret: CLAUDE.md", "shortDescription": {"text": "Agent instruction/config may expose a secret: CLAUDE.md"}, "fullDescription": {"text": "Agent-facing files are routinely pasted into LLM/tool contexts. Move literal tokens, keys, and passwords into a secret manager or document them as placeholders only."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-af5de01514bd2a55", "name": "Commented-code block (5 lines) in frontend/next.config.js:3", "shortDescription": {"text": "Commented-code block (5 lines) in frontend/next.config.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-19bc633102e4d476", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/hwp/HwpEditor.tsx:101", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/hwp/HwpEditor.tsx:101"}, "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-91b1685fb278da03", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/drive/DriveBackupActions.tsx:36", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/drive/DriveBackupActions.tsx:36"}, "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-cb6d35e1a8176fe7", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step8Supervisors.tsx:30", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step8Supervisors.tsx:30"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d5086187425b4267", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step5Students.tsx:93", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step5Students.tsx:93"}, "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-44cb59a46c5a92ec", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step4Teachers.tsx:121", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step4Teachers.tsx:121"}, "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-3b9052e1d2087c85", "name": "Legacy-named symbol `collab_precaution_seen_v1` in frontend/src/components/collab/CollabPrecautionModal.tsx:25", "shortDescription": {"text": "Legacy-named symbol `collab_precaution_seen_v1` in frontend/src/components/collab/CollabPrecautionModal.tsx:25"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-15ca9a23993b534f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/users/_components/CsvBulkImportModal.tsx:43", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/users/_components/CsvBulkImportModal.tsx:43"}, "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-05288b5658cbef9c", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/page.tsx:114", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/page.tsx:114"}, "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-63452e544796e18e", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/import/page.tsx:26", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/import/page.tsx:26"}, "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-56493d7e14150275", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/updates/page.tsx:125", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/updates/page.tsx:125"}, "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-fc52ee6e0549d0aa", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/enrollments/_components/CsvUploadModal.tsx", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/enrollments/_components/CsvUploadModal.tsx:51"}, "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-61f0e44745a2bae4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormModal.ts", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormModal.tsx:91"}, "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-e456e78a060ab369", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/research-supervisors/page.tsx:155", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/research-supervisors/page.tsx:155"}, "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-081dd7bbe6b34e42", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/[sid]/_components/Respons", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/[sid]/_components/ResponsesTab.tsx:68"}, "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-5fa6ee0628d6b4ec", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/q/[slug]/page.tsx:38", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/q/[slug]/page.tsx:38"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-105671a3459e1706", "name": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.tsx:94", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.tsx:94"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-736772b15bf2070c", "name": "Legacy-named symbol `test_restore_warns_on_extra_table_in_backup` in backend/tests/test_backup_roundtrip.py:143", "shortDescription": {"text": "Legacy-named symbol `test_restore_warns_on_extra_table_in_backup` in backend/tests/test_backup_roundtrip.py:143"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1d16c4dde649971f", "name": "Legacy-named symbol `test_v2` in backend/tests/test_filename_normalize.py:175", "shortDescription": {"text": "Legacy-named symbol `test_v2` in backend/tests/test_filename_normalize.py:175"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-02e9eb2223159e70", "name": "Commented-code block (5 lines) in backend/tests/test_security_guards.py:244", "shortDescription": {"text": "Commented-code block (5 lines) in backend/tests/test_security_guards.py:244"}, "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-8b7b2a887633fc8f", "name": "Legacy-named symbol `start_backup` in backend/app/main.py:80", "shortDescription": {"text": "Legacy-named symbol `start_backup` in backend/app/main.py:80"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fb058039830ac4fd", "name": "Commented-code block (5 lines) in backend/app/main.py:107", "shortDescription": {"text": "Commented-code block (5 lines) in backend/app/main.py: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-9727b27d1d43f962", "name": "Commented-code block (6 lines) in backend/app/models/classroom.py:161", "shortDescription": {"text": "Commented-code block (6 lines) in backend/app/models/classroom.py: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-bbc6ff9effe473f9", "name": "Commented-code block (8 lines) in backend/app/models/archive.py:116", "shortDescription": {"text": "Commented-code block (8 lines) in backend/app/models/archive.py:116"}, "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-f25e6dda1a449b1a", "name": "Commented-code block (5 lines) in backend/app/models/user.py:34", "shortDescription": {"text": "Commented-code block (5 lines) in backend/app/models/user.py:34"}, "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-b8bd6ba7212e31ae", "name": "Commented-code block (5 lines) in backend/app/modules/drive/organize.py:365", "shortDescription": {"text": "Commented-code block (5 lines) in backend/app/modules/drive/organize.py:365"}, "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-d070ee24917960a4", "name": "Legacy-named symbol `download_my_drive_backup` in backend/app/modules/drive/backup.py:688", "shortDescription": {"text": "Legacy-named symbol `download_my_drive_backup` in backend/app/modules/drive/backup.py:688"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-29f9b5e6b364ccac", "name": "Commented-code block (6 lines) in backend/app/modules/club/router.py:198", "shortDescription": {"text": "Commented-code block (6 lines) in backend/app/modules/club/router.py:198"}, "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-8c0d3ac97959c8b4", "name": "Legacy-named symbol `export_backup` in backend/app/modules/system/backup.py:33", "shortDescription": {"text": "Legacy-named symbol `export_backup` in backend/app/modules/system/backup.py:33"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c4277284f5366a0a", "name": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/router.py:731", "shortDescription": {"text": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/router.py:731"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-bb594bd17240f7b2", "name": "Commented-code block (7 lines) in backend/app/modules/classroom/schemas.py:15", "shortDescription": {"text": "Commented-code block (7 lines) in backend/app/modules/classroom/schemas.py:15"}, "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-75b86d55bc9472bd", "name": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/student_copy.py:15", "shortDescription": {"text": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/student_copy.py:15"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6c2b36cc0fa19706", "name": "Network/subprocess call without timeout or try/except \u2014 backend/app/modules/ai_developer/service.py:481", "shortDescription": {"text": "Network/subprocess call without timeout or try/except \u2014 backend/app/modules/ai_developer/service.py:481"}, "fullDescription": {"text": "`subprocess.Popen(...)` here lacks both a `timeout=` arg and an enclosing try/except. This is exactly the class of bug that took down our git-clone earlier (HTTP/2 stream cancel surfaced as a fatal). Add a `timeout=` and wrap in try/except, or use a wrapper that retries."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e631eb17aa658cd6", "name": "Legacy-named symbol `student_copy` in backend/app/services/attachment_share.py:8", "shortDescription": {"text": "Legacy-named symbol `student_copy` in backend/app/services/attachment_share.py:8"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ab99e6037bd650de", "name": "Legacy-named symbol `deleted_old` in backend/app/core/backup_scheduler.py:238", "shortDescription": {"text": "Legacy-named symbol `deleted_old` in backend/app/core/backup_scheduler.py:238"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ee6f2d3b20bd1c82", "name": "Commented-code block (7 lines) in backend/app/core/config.py:99", "shortDescription": {"text": "Commented-code block (7 lines) in backend/app/core/config.py:99"}, "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-371280905d3f03ea", "name": "Legacy-named symbol `truly_unused` in backend/app/core/permission_registry.py:94", "shortDescription": {"text": "Legacy-named symbol `truly_unused` in backend/app/core/permission_registry.py:94"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5bf3f52a5a5d6967", "name": "Commented-code block (6 lines) in backend/app/core/files.py:127", "shortDescription": {"text": "Commented-code block (6 lines) in backend/app/core/files.py:127"}, "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-9e8da0591b6fca89", "name": "Commented-code block (6 lines) in backend/app/core/permissions.py:160", "shortDescription": {"text": "Commented-code block (6 lines) in backend/app/core/permissions.py:160"}, "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-c331f928552f5d43", "name": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/auth.ts:94", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/auth.ts:94"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8743ea4b971a9d43", "name": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/storage.ts:17", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/storage.ts:17"}, "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-022e6399d1ef2beb", "name": "12 env vars used in code but missing from .env.example", "shortDescription": {"text": "12 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `BACKEND_PROXY_URL`, `BACKUP_DEST`, `CSRF_ENFORCE`, `GITHUB_UPDATE_BRANCH`, `GITHUB_UPDATE_REPO`, `GITHUB_UPDATE_TOKEN`, `INTERNAL_API_URL`, `NEXT_PUBLIC_API_URL` + 4 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-2c04133e54348533", "name": "Near-duplicate function bodies in 2 places", "shortDescription": {"text": "Near-duplicate function bodies in 2 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\nbackend/app/main.py:health, backend/app/modules/system/router.py:health\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-49c98f7cedd9c977", "name": "Near-duplicate function bodies in 4 places", "shortDescription": {"text": "Near-duplicate function bodies in 4 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\nbackend/app/modules/drive/router.py:soft_delete_item, backend/app/modules/drive/router.py:restore_item, backend/app/modules/drive/router.py:permanent_delete_item, backend/app/modules/drive/favorites.py:toggle_favorite\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8bc0a6c3e8884516", "name": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_whiteboard/router.py:420", "shortDescription": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_whiteboard/router.py:420"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-b18243180feabc0f", "name": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_sheets/router.py:478", "shortDescription": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_sheets/router.py:478"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-02dfc83a70eed161", "name": "FastAPI POST `refresh` without auth dependency \u2014 backend/app/modules/auth/session.py:30", "shortDescription": {"text": "FastAPI POST `refresh` without auth dependency \u2014 backend/app/modules/auth/session.py:30"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-d37dbf4b184ee65c", "name": "FastAPI POST `verify_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:224", "shortDescription": {"text": "FastAPI POST `verify_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:224"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-611bc4c337985e38", "name": "FastAPI POST `resend_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:307", "shortDescription": {"text": "FastAPI POST `resend_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:307"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-d78057145ab2d40d", "name": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_slides/hocuspocus.py:63", "shortDescription": {"text": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_slides/hocuspocus.py:63"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-8401645f4c5e4e46", "name": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_board/router.py:565", "shortDescription": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_board/router.py:565"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-7571ac0e003f495b", "name": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_docs/hocuspocus.py:71", "shortDescription": {"text": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_docs/hocuspocus.py:71"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-e6e2883e32144bb3", "name": "Dangling fetch: POST /api/students/_io/import/${type}?dry_run=${dryRun} (frontend/src/app/(admin)/students/import/page.t", "shortDescription": {"text": "Dangling fetch: POST /api/students/_io/import/${type}?dry_run=${dryRun} (frontend/src/app/(admin)/students/import/page.tsx:42)"}, "fullDescription": {"text": "`frontend/src/app/(admin)/students/import/page.tsx:42` calls `POST /api/students/_io/import/${type}?dry_run=${dryRun}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/students/_io/import/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f7108c6e2f86a988", "name": "Dangling fetch: PATCH /api/feedback/${id} (frontend/src/app/(admin)/system/feedback/page.tsx:57)", "shortDescription": {"text": "Dangling fetch: PATCH /api/feedback/${id} (frontend/src/app/(admin)/system/feedback/page.tsx:57)"}, "fullDescription": {"text": "`frontend/src/app/(admin)/system/feedback/page.tsx:57` calls `PATCH /api/feedback/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/feedback/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-4756b4c4da7d2088", "name": "Unused endpoint: GET /api/health", "shortDescription": {"text": "Unused endpoint: GET /api/health"}, "fullDescription": {"text": "`backend/app/main.py` declares `GET /api/health` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5baa8971ebe192a1", "name": "Unused endpoint: GET /", "shortDescription": {"text": "Unused endpoint: GET /"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7a009b1a56794f45", "name": "Unused endpoint: POST /", "shortDescription": {"text": "Unused endpoint: POST /"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `POST /` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-84e8012733260152", "name": "Unused endpoint: PUT /{wid}", "shortDescription": {"text": "Unused endpoint: PUT /{wid}"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `PUT /{wid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f2affefa676c9ef2", "name": "Unused endpoint: DELETE /{wid}", "shortDescription": {"text": "Unused endpoint: DELETE /{wid}"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `DELETE /{wid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c2df71d5e1393d57", "name": "Unused endpoint: GET /shared-with-me", "shortDescription": {"text": "Unused endpoint: GET /shared-with-me"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /shared-with-me` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4b46fcd7564beb19", "name": "Unused endpoint: GET /{wid}/shares", "shortDescription": {"text": "Unused endpoint: GET /{wid}/shares"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /{wid}/shares` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-77a8c3fa0a83b3ef", "name": "Unused endpoint: POST /{wid}/shares", "shortDescription": {"text": "Unused endpoint: POST /{wid}/shares"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `POST /{wid}/shares` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-042262e1565065fa", "name": "Unused endpoint: DELETE /{wid}/shares/{share_id}", "shortDescription": {"text": "Unused endpoint: DELETE /{wid}/shares/{share_id}"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `DELETE /{wid}/shares/{share_id}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-741ca724c77e3a50", "name": "Unused endpoint: POST /{wid}/duplicate", "shortDescription": {"text": "Unused endpoint: POST /{wid}/duplicate"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `POST /{wid}/duplicate` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4fbbb30e149acbe0", "name": "Unused endpoint: GET /{wid}", "shortDescription": {"text": "Unused endpoint: GET /{wid}"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /{wid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-69d26da2b41e20dd", "name": "Unused endpoint: GET /{wid}/permission", "shortDescription": {"text": "Unused endpoint: GET /{wid}/permission"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /{wid}/permission` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-35b8d1979c5bca07", "name": "Unused endpoint: GET /{wid}/yjs-snapshot", "shortDescription": {"text": "Unused endpoint: GET /{wid}/yjs-snapshot"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `GET /{wid}/yjs-snapshot` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7250c435597fbc88", "name": "Unused endpoint: POST /{wid}/yjs-snapshot", "shortDescription": {"text": "Unused endpoint: POST /{wid}/yjs-snapshot"}, "fullDescription": {"text": "`backend/app/modules/tool_whiteboard/router.py` declares `POST /{wid}/yjs-snapshot` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-56e3411959f4c471", "name": "Unused endpoint: GET /{sid}", "shortDescription": {"text": "Unused endpoint: GET /{sid}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /{sid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9c138d64a0584bde", "name": "Unused endpoint: PUT /{sid}", "shortDescription": {"text": "Unused endpoint: PUT /{sid}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `PUT /{sid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f0ec7c44529c217a", "name": "Unused endpoint: DELETE /{sid}", "shortDescription": {"text": "Unused endpoint: DELETE /{sid}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `DELETE /{sid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-df46e72dad75b2ac", "name": "Unused endpoint: GET /{sid}/members", "shortDescription": {"text": "Unused endpoint: GET /{sid}/members"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /{sid}/members` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b34280d434a4c879", "name": "Unused endpoint: POST /{sid}/members", "shortDescription": {"text": "Unused endpoint: POST /{sid}/members"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `POST /{sid}/members` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5fe781793fc8a768", "name": "Unused endpoint: DELETE /{sid}/members/{mid}", "shortDescription": {"text": "Unused endpoint: DELETE /{sid}/members/{mid}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `DELETE /{sid}/members/{mid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0f5bee8a91c7102d", "name": "Unused endpoint: GET /{sid}/snapshot-state", "shortDescription": {"text": "Unused endpoint: GET /{sid}/snapshot-state"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /{sid}/snapshot-state` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1681087120e2a279", "name": "Unused endpoint: POST /{sid}/snapshot-state", "shortDescription": {"text": "Unused endpoint: POST /{sid}/snapshot-state"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `POST /{sid}/snapshot-state` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-93cc38716dc089e1", "name": "Unused endpoint: GET /{sid}/permission", "shortDescription": {"text": "Unused endpoint: GET /{sid}/permission"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /{sid}/permission` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0e20b7af493c8f0d", "name": "Unused endpoint: GET /{sid}/yjs-snapshot", "shortDescription": {"text": "Unused endpoint: GET /{sid}/yjs-snapshot"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /{sid}/yjs-snapshot` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-35c35b01eed84801", "name": "Unused endpoint: POST /{sid}/yjs-snapshot", "shortDescription": {"text": "Unused endpoint: POST /{sid}/yjs-snapshot"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `POST /{sid}/yjs-snapshot` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2e64ea31216de42a", "name": "Unused endpoint: POST /_from-survey/{survey_id}", "shortDescription": {"text": "Unused endpoint: POST /_from-survey/{survey_id}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `POST /_from-survey/{survey_id}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5f802dd74f719e97", "name": "Unused endpoint: GET /_survey-data/{survey_id}", "shortDescription": {"text": "Unused endpoint: GET /_survey-data/{survey_id}"}, "fullDescription": {"text": "`backend/app/modules/classroom_sheets/router.py` declares `GET /_survey-data/{survey_id}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b15d518d538de57d", "name": "Unused endpoint: GET /sessions", "shortDescription": {"text": "Unused endpoint: GET /sessions"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `GET /sessions` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-77e497414a27bc4a", "name": "Unused endpoint: GET /sessions/{sid}", "shortDescription": {"text": "Unused endpoint: GET /sessions/{sid}"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `GET /sessions/{sid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2d50d060a6759dd1", "name": "Unused endpoint: POST /sessions/{sid}/start", "shortDescription": {"text": "Unused endpoint: POST /sessions/{sid}/start"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /sessions/{sid}/start` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-58eb99430326c227", "name": "Unused endpoint: POST /sessions/{sid}/goto", "shortDescription": {"text": "Unused endpoint: POST /sessions/{sid}/goto"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /sessions/{sid}/goto` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-aaa82271e440d1e3", "name": "Unused endpoint: POST /sessions/{sid}/end", "shortDescription": {"text": "Unused endpoint: POST /sessions/{sid}/end"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /sessions/{sid}/end` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-79437adc194451bb", "name": "Unused endpoint: GET /sessions/{sid}/qr.png", "shortDescription": {"text": "Unused endpoint: GET /sessions/{sid}/qr.png"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `GET /sessions/{sid}/qr.png` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-633d0a653ebbf1bf", "name": "Unused endpoint: POST /join", "shortDescription": {"text": "Unused endpoint: POST /join"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /join` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-26d7690fd478981c", "name": "Unused endpoint: GET /play/{sid}/state", "shortDescription": {"text": "Unused endpoint: GET /play/{sid}/state"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `GET /play/{sid}/state` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ec65dd7db4438f8e", "name": "Unused endpoint: POST /play/{sid}/respond", "shortDescription": {"text": "Unused endpoint: POST /play/{sid}/respond"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /play/{sid}/respond` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6b7100fe091a61c3", "name": "Unused endpoint: GET /{pid}", "shortDescription": {"text": "Unused endpoint: GET /{pid}"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `GET /{pid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d25a3163609d8afb", "name": "Unused endpoint: PUT /{pid}", "shortDescription": {"text": "Unused endpoint: PUT /{pid}"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `PUT /{pid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f2c97ea12d1c0b2b", "name": "Unused endpoint: DELETE /{pid}", "shortDescription": {"text": "Unused endpoint: DELETE /{pid}"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `DELETE /{pid}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4b640ecf415b7200", "name": "Unused endpoint: POST /{pid}/sessions", "shortDescription": {"text": "Unused endpoint: POST /{pid}/sessions"}, "fullDescription": {"text": "`backend/app/modules/tool_poll/router.py` declares `POST /{pid}/sessions` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ac3f3e641cad22ed", "name": "Unused endpoint: POST /_quota/bulk", "shortDescription": {"text": "Unused endpoint: POST /_quota/bulk"}, "fullDescription": {"text": "`backend/app/modules/users/quota.py` declares `POST /_quota/bulk` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f910390613da27ca", "name": "Unused endpoint: POST /{user_id}/quota", "shortDescription": {"text": "Unused endpoint: POST /{user_id}/quota"}, "fullDescription": {"text": "`backend/app/modules/users/quota.py` declares `POST /{user_id}/quota` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-bef922eb7c9f3ceb", "name": "Unused endpoint: PATCH /{user_id}/lifecycle", "shortDescription": {"text": "Unused endpoint: PATCH /{user_id}/lifecycle"}, "fullDescription": {"text": "`backend/app/modules/users/lifecycle.py` declares `PATCH /{user_id}/lifecycle` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-a50e09dd0a98cec7", "name": "Unused endpoint: POST /{user_id}/transfer-ownership", "shortDescription": {"text": "Unused endpoint: POST /{user_id}/transfer-ownership"}, "fullDescription": {"text": "`backend/app/modules/users/lifecycle.py` declares `POST /{user_id}/transfer-ownership` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c368dede2e2f5cee", "name": "Unused endpoint: GET /peers", "shortDescription": {"text": "Unused endpoint: GET /peers"}, "fullDescription": {"text": "`backend/app/modules/users/crud.py` declares `GET /peers` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3cdc30f2b9308e3f", "name": "Unused endpoint: PUT /{user_id}", "shortDescription": {"text": "Unused endpoint: PUT /{user_id}"}, "fullDescription": {"text": "`backend/app/modules/users/crud.py` declares `PUT /{user_id}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-55c93ee7e031a509", "name": "Unused endpoint: DELETE /{user_id}", "shortDescription": {"text": "Unused endpoint: DELETE /{user_id}"}, "fullDescription": {"text": "`backend/app/modules/users/crud.py` declares `DELETE /{user_id}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f2e38a416ddc468f", "name": "Unused endpoint: GET /excel-template", "shortDescription": {"text": "Unused endpoint: GET /excel-template"}, "fullDescription": {"text": "`backend/app/modules/users/bulk.py` declares `GET /excel-template` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ebd27a62b6856686", "name": "Unused endpoint: POST /bulk-import/validate", "shortDescription": {"text": "Unused endpoint: POST /bulk-import/validate"}, "fullDescription": {"text": "`backend/app/modules/users/bulk.py` declares `POST /bulk-import/validate` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-5b522763d542fab7", "name": "Unused endpoint: POST /bulk-import/confirm", "shortDescription": {"text": "Unused endpoint: POST /bulk-import/confirm"}, "fullDescription": {"text": "`backend/app/modules/users/bulk.py` declares `POST /bulk-import/confirm` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}]}}, "automationDetails": {"id": "repobility/19724"}, "properties": {"repository": "sinbc2003/general_school", "repoUrl": "https://github.com/sinbc2003/general_school", "branch": "main"}, "results": [{"ruleId": "scanner-a3725ae325aca13d", "level": "note", "message": {"text": "Possibly dead Python function: run_seeds"}, "properties": {"repobilityId": "b821bd1125f96bf3", "scanner": "scanner-primary", "fingerprint": "a3725ae325aca13d", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/scripts/seed.py:198"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d45e5050fc15bf1e", "level": "note", "message": {"text": "Possibly dead Python function: cleanup_shares"}, "properties": {"repobilityId": "99f0b56283a4b806", "scanner": "scanner-primary", "fingerprint": "d45e5050fc15bf1e", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/tool_share.py:108"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-f8da5d8917c4c097", "level": "note", "message": {"text": "Possibly dead Python function: jaccard"}, "properties": {"repobilityId": "0796472cb2d82932", "scanner": "scanner-primary", "fingerprint": "f8da5d8917c4c097", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/text_similarity.py:20"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-af52997b5f40622b", "level": "note", "message": {"text": "Possibly dead Python function: sync_all_users_background"}, "properties": {"repobilityId": "1d3a92303d17b7f8", "scanner": "scanner-primary", "fingerprint": "af52997b5f40622b", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/folder_seed.py:501"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-97be83e3d05677c8", "level": "note", "message": {"text": "Possibly dead Python function: archive_prev_semester_background"}, "properties": {"repobilityId": "9c4547af056e465e", "scanner": "scanner-primary", "fingerprint": "97be83e3d05677c8", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/folder_seed.py:614"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-7058450ee2f43a54", "level": "note", "message": {"text": "Possibly dead Python function: generate_student_pdf"}, "properties": {"repobilityId": "0bce0ce628a55882", "scanner": "scanner-primary", "fingerprint": "7058450ee2f43a54", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/report_pdf.py:217"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-a5273fcc857fa038", "level": "note", "message": {"text": "Possibly dead Python function: resolve_grader_model"}, "properties": {"repobilityId": "817e99b919456f7f", "scanner": "scanner-primary", "fingerprint": "a5273fcc857fa038", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/llm_grader.py:277"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-0a654e56d3f5ac61", "level": "note", "message": {"text": "Possibly dead Python function: grade_pending_for_student"}, "properties": {"repobilityId": "d81f0bd708fee67f", "scanner": "scanner-primary", "fingerprint": "0a654e56d3f5ac61", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/llm_grader.py:470"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-b552fc92bcf0dab7", "level": "note", "message": {"text": "Possibly dead Python function: grade_problem_set"}, "properties": {"repobilityId": "78ba9be550fdf007", "scanner": "scanner-primary", "fingerprint": "b552fc92bcf0dab7", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/courseware_grader.py:143"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-292ee5340181940a", "level": "note", "message": {"text": "Possibly dead Python function: restart_services"}, "properties": {"repobilityId": "7d34d3c87d21bb58", "scanner": "scanner-primary", "fingerprint": "292ee5340181940a", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/services/updates/steps.py:357"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-99f94c1e9416db3b", "level": "note", "message": {"text": "Possibly dead Python function: start_scheduler"}, "properties": {"repobilityId": "0591c4bd684fd589", "scanner": "scanner-primary", "fingerprint": "99f94c1e9416db3b", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/backup_scheduler.py:294"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-db3fb1492380b51a", "level": "note", "message": {"text": "Possibly dead Python function: start_scheduler"}, "properties": {"repobilityId": "0591c4bd684fd589", "scanner": "scanner-primary", "fingerprint": "db3fb1492380b51a", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/notification_scheduler.py:657"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-06d916fb58e2eece", "level": "note", "message": {"text": "Possibly dead Python function: require_role"}, "properties": {"repobilityId": "ebb52713535182e2", "scanner": "scanner-primary", "fingerprint": "06d916fb58e2eece", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/auth.py:78"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-f02306b786f85fca", "level": "note", "message": {"text": "Possibly dead Python function: safe_storage_filename"}, "properties": {"repobilityId": "8a93e9db1d7b4d40", "scanner": "scanner-primary", "fingerprint": "f02306b786f85fca", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/upload.py:218"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-120c8532c0ad70c0", "level": "note", "message": {"text": "Possibly dead Python function: verify_csrf"}, "properties": {"repobilityId": "f6974d4d2716829e", "scanner": "scanner-primary", "fingerprint": "120c8532c0ad70c0", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/csrf.py:90"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-9baf38c3740b6a3d", "level": "note", "message": {"text": "Possibly dead Python function: is_feature_enabled"}, "properties": {"repobilityId": "3cbf71746d6263e2", "scanner": "scanner-primary", "fingerprint": "9baf38c3740b6a3d", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/core/features.py:111"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-3c0c862d7fd26729", "level": "note", "message": {"text": "Possibly dead Python function: include_object"}, "properties": {"repobilityId": "425a5e67292c778e", "scanner": "scanner-primary", "fingerprint": "3c0c862d7fd26729", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/alembic/env.py:50"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d1abbd7a8c8f523e", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 frontend/scripts/copy-standalone.js:15"}, "properties": {"repobilityId": "f545ab65d1dfae72", "scanner": "scanner-primary", "fingerprint": "d1abbd7a8c8f523e", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-6e6855b3f7642d6e", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/StudentPickerModal.tsx:253"}, "properties": {"repobilityId": "6dbb1208ffe3985c", "scanner": "scanner-primary", "fingerprint": "6e6855b3f7642d6e", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-147df8e37526f6a7", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 frontend/src/components/timetable/MyEventsModal.tsx:149"}, "properties": {"repobilityId": "db8e0fe167c4e38b", "scanner": "scanner-primary", "fingerprint": "147df8e37526f6a7", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-f8f2bc1941ea105a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/timetable/MyEventsModal.tsx:238"}, "properties": {"repobilityId": "e7f11f27ecfca482", "scanner": "scanner-primary", "fingerprint": "f8f2bc1941ea105a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b694b08e51225455", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/courseware/ProblemContent.tsx:157"}, "properties": {"repobilityId": "b2e82c60f36b5591", "scanner": "scanner-primary", "fingerprint": "b694b08e51225455", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-11a0279626fd7aa6", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/courseware/MyCoursewareView.tsx:434"}, "properties": {"repobilityId": "3fdc773676cc87e1", "scanner": "scanner-primary", "fingerprint": "11a0279626fd7aa6", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4ad862e95cbd5f02", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/PresentMode.tsx:121"}, "properties": {"repobilityId": "64a67d2fa8d72dd5", "scanner": "scanner-primary", "fingerprint": "4ad862e95cbd5f02", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4dfe91c309f64d50", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/DeckEditor.tsx:223"}, "properties": {"repobilityId": "8100a927928659da", "scanner": "scanner-primary", "fingerprint": "4dfe91c309f64d50", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a44e25069aa5a708", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/decks/ThemePicker.tsx:94"}, "properties": {"repobilityId": "d542d014a1f99ffd", "scanner": "scanner-primary", "fingerprint": "a44e25069aa5a708", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-2e5c1f7f0a2cd842", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/docs/SlashCommand.tsx:212"}, "properties": {"repobilityId": "391559ab63a62aef", "scanner": "scanner-primary", "fingerprint": "2e5c1f7f0a2cd842", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-90a08e6ccc888021", "level": "note", "message": {"text": "\"active\" state uses light bg in a dark theme \u2014 frontend/src/components/docs/SlashCommand.tsx:207"}, "properties": {"repobilityId": "f0a2a77e32d8c5db", "scanner": "scanner-primary", "fingerprint": "90a08e6ccc888021", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.active-light-bg"]}}, {"ruleId": "scanner-04cf0a8c41a5ab70", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 frontend/src/components/docs/CollabEditor.tsx:98"}, "properties": {"repobilityId": "1ed0230a79af1b6d", "scanner": "scanner-primary", "fingerprint": "04cf0a8c41a5ab70", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-f648baa5a6024261", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 frontend/src/components/ui/Toast.tsx:43"}, "properties": {"repobilityId": "08367d5e91b44a6b", "scanner": "scanner-primary", "fingerprint": "f648baa5a6024261", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-cd7125b6d226bea2", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/workspace/MyWorkspaceView.tsx:206"}, "properties": {"repobilityId": "0c8a6a95f153e9fa", "scanner": "scanner-primary", "fingerprint": "cd7125b6d226bea2", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b6e63a2786857802", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/MoveToFolderModal.tsx:118"}, "properties": {"repobilityId": "2af029753d98b4a0", "scanner": "scanner-primary", "fingerprint": "b6e63a2786857802", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-99f8134cad07b3e9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/ItemCard.tsx:102"}, "properties": {"repobilityId": "0f8dcc4ac5d254f9", "scanner": "scanner-primary", "fingerprint": "99f8134cad07b3e9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d581dee0e61a3f72", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/DrivePage.tsx:544"}, "properties": {"repobilityId": "ae1b62dc1dcd1962", "scanner": "scanner-primary", "fingerprint": "d581dee0e61a3f72", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1ff626e20ee76f4a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderSidebar.tsx:201"}, "properties": {"repobilityId": "eb1cf03d97ccf058", "scanner": "scanner-primary", "fingerprint": "1ff626e20ee76f4a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4dc9b9548c4fd535", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/FolderCard.tsx:66"}, "properties": {"repobilityId": "79572cc6f427798c", "scanner": "scanner-primary", "fingerprint": "4dc9b9548c4fd535", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e1e86815d09a52dd", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/drive/GoogleDriveSidePanel.tsx:178"}, "properties": {"repobilityId": "eaca76e5ee734d0e", "scanner": "scanner-primary", "fingerprint": "e1e86815d09a52dd", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-50b3e1b531e9c34f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/chat/SessionGroup.tsx:89"}, "properties": {"repobilityId": "85f622659e8fc41f", "scanner": "scanner-primary", "fingerprint": "50b3e1b531e9c34f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-218584610fc4ee7f", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 frontend/src/components/admin/ClubAssignmentModal.tsx:70"}, "properties": {"repobilityId": "82eda44e91e7dab4", "scanner": "scanner-primary", "fingerprint": "218584610fc4ee7f", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-67b8b3d36158b0ff", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/sidebar.tsx:231"}, "properties": {"repobilityId": "82f64212d39463eb", "scanner": "scanner-primary", "fingerprint": "67b8b3d36158b0ff", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c4238d30c822fdab", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/UserSearchInput.tsx:148"}, "properties": {"repobilityId": "0db2a322963aae22", "scanner": "scanner-primary", "fingerprint": "c4238d30c822fdab", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5867898dd7faa5c5", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/admin/EnrollmentPositionsModal.tsx:170"}, "properties": {"repobilityId": "44d12b7a9623a505", "scanner": "scanner-primary", "fingerprint": "5867898dd7faa5c5", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c2334ee614b48dae", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/whiteboard/WhiteboardCanvas.tsx:527"}, "properties": {"repobilityId": "fa0bc4ff82027342", "scanner": "scanner-primary", "fingerprint": "c2334ee614b48dae", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-80e2f0cc1f7d2940", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/wordbook/StudyView.tsx:273"}, "properties": {"repobilityId": "28fec2a80832aebd", "scanner": "scanner-primary", "fingerprint": "80e2f0cc1f7d2940", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e00f2ce2ec0a4926", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostDetailView.tsx:435"}, "properties": {"repobilityId": "84116c61b42f1ce8", "scanner": "scanner-primary", "fingerprint": "e00f2ce2ec0a4926", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-68e88d2fca237c41", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/DrivePicker.tsx:171"}, "properties": {"repobilityId": "36bc6e346b7c5b9e", "scanner": "scanner-primary", "fingerprint": "68e88d2fca237c41", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9c585ae6b1fef215", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/UserPicker.tsx:266"}, "properties": {"repobilityId": "6fe368b476c19b70", "scanner": "scanner-primary", "fingerprint": "9c585ae6b1fef215", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-121e97a85f89b3a7", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ReusePostModal.tsx:140"}, "properties": {"repobilityId": "49c957c81cdb00f7", "scanner": "scanner-primary", "fingerprint": "121e97a85f89b3a7", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7f0d27e24a084758", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/PostStreamCard.tsx:97"}, "properties": {"repobilityId": "b7597516afc799af", "scanner": "scanner-primary", "fingerprint": "7f0d27e24a084758", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-da13ccb5caf541ce", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/GoogleDocPreview.tsx:77"}, "properties": {"repobilityId": "7b5349b2581f1794", "scanner": "scanner-primary", "fingerprint": "da13ccb5caf541ce", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-979cf7326f593371", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/SubmissionPanel.tsx:298"}, "properties": {"repobilityId": "0428d0a41e94ec04", "scanner": "scanner-primary", "fingerprint": "979cf7326f593371", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a5657a65ef4127be", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ChatbotPickerModal.tsx:92"}, "properties": {"repobilityId": "b3a05c466307a554", "scanner": "scanner-primary", "fingerprint": "a5657a65ef4127be", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-abe1efeddf101fb0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseChatbots.tsx:149"}, "properties": {"repobilityId": "dd6f0d5d0f93aa8b", "scanner": "scanner-primary", "fingerprint": "abe1efeddf101fb0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-26126f3355d50980", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/BoardPickerModal.tsx:115"}, "properties": {"repobilityId": "074a0b8f531ced6d", "scanner": "scanner-primary", "fingerprint": "26126f3355d50980", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6dd38715efaccd23", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareLinkModal.tsx:128"}, "properties": {"repobilityId": "586fd5a02f2da610", "scanner": "scanner-primary", "fingerprint": "6dd38715efaccd23", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-62716ad3d2130ff1", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/QuizPickerModal.tsx:99"}, "properties": {"repobilityId": "9c94fca8c8aca94d", "scanner": "scanner-primary", "fingerprint": "62716ad3d2130ff1", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-398b671380a37830", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WordDeckPickerModal.tsx:115"}, "properties": {"repobilityId": "a62ee5cdee623050", "scanner": "scanner-primary", "fingerprint": "398b671380a37830", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f8e1f98bc48c1dc8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/CourseInfoWidget.tsx:76"}, "properties": {"repobilityId": "ba04e6a9854ed4f4", "scanner": "scanner-primary", "fingerprint": "f8e1f98bc48c1dc8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-18a128c4409bfdbd", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/WhiteboardPickerModal.tsx:111"}, "properties": {"repobilityId": "26388ae092614f36", "scanner": "scanner-primary", "fingerprint": "18a128c4409bfdbd", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d240b60ddd7feede", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/ShareDocModal.tsx:185"}, "properties": {"repobilityId": "d0646c3ccfd6cac3", "scanner": "scanner-primary", "fingerprint": "d240b60ddd7feede", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-58fed7699e99726d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/classroom/AssignmentModal.tsx:617"}, "properties": {"repobilityId": "92f3b60b7b99c869", "scanner": "scanner-primary", "fingerprint": "58fed7699e99726d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-19a2944906c5ec32", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/tools/ToolShareModal.tsx:72"}, "properties": {"repobilityId": "bcf77cb75cab2bb8", "scanner": "scanner-primary", "fingerprint": "19a2944906c5ec32", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9cbd3aacf2cdbae2", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/components/board/BoardView.tsx:253"}, "properties": {"repobilityId": "5d284d6c47edea65", "scanner": "scanner-primary", "fingerprint": "9cbd3aacf2cdbae2", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ce0d7c841da809db", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/layout.tsx:84"}, "properties": {"repobilityId": "109d64970a1be085", "scanner": "scanner-primary", "fingerprint": "ce0d7c841da809db", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-34dad86985a2c319", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/page.tsx:126"}, "properties": {"repobilityId": "db1a0972531c992c", "scanner": "scanner-primary", "fingerprint": "34dad86985a2c319", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f63755bf2346df6c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/record-writer/[id]/page.tsx:608"}, "properties": {"repobilityId": "de45f6d2c7a9ecf4", "scanner": "scanner-primary", "fingerprint": "f63755bf2346df6c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b1a9b901859ec599", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/admissions/analysis/page.tsx:84"}, "properties": {"repobilityId": "9afde27208e56fa7", "scanner": "scanner-primary", "fingerprint": "b1a9b901859ec599", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-990b82753405d113", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/dashboard/page.tsx:282"}, "properties": {"repobilityId": "c5877c15aa4866ab", "scanner": "scanner-primary", "fingerprint": "990b82753405d113", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-a0ed6994c05cff91", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/_tabs/ArtifactsTab.tsx:63"}, "properties": {"repobilityId": "f3d4829dffeacee9", "scanner": "scanner-primary", "fingerprint": "a0ed6994c05cff91", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f8090ba8b1806ed6", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/students/artifacts-gallery/page.tsx:107"}, "properties": {"repobilityId": "a053188ddf29f047", "scanner": "scanner-primary", "fingerprint": "f8090ba8b1806ed6", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b40c0f5b03db8ae3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/announcements/page.tsx:253"}, "properties": {"repobilityId": "5b5bfaa9b5df0970", "scanner": "scanner-primary", "fingerprint": "b40c0f5b03db8ae3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3eca99ac5a43dd99", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/past-research/page.tsx:457"}, "properties": {"repobilityId": "4f8c0e8fc16c40f5", "scanner": "scanner-primary", "fingerprint": "3eca99ac5a43dd99", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-01dd9fc9b46c4e5b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/departments/[id]/delegations/page.tsx:158"}, "properties": {"repobilityId": "f2696683fb0ef398", "scanner": "scanner-primary", "fingerprint": "01dd9fc9b46c4e5b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5251b6bd09855c5c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/backup/_components/BackupSchedule.tsx:301"}, "properties": {"repobilityId": "608ccbc0beb010e0", "scanner": "scanner-primary", "fingerprint": "5251b6bd09855c5c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-2718a5eeaf47a28f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/ai-developer/page.tsx:230"}, "properties": {"repobilityId": "8032e915d1f93e7e", "scanner": "scanner-primary", "fingerprint": "2718a5eeaf47a28f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c3eda40738e9f189", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormModal.tsx:410"}, "properties": {"repobilityId": "38d7c7d0ddf069c6", "scanner": "scanner-primary", "fingerprint": "c3eda40738e9f189", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7bb226039f88ecf7", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/courseware/[psid]/page.tsx:116"}, "properties": {"repobilityId": "5caea8a2ce001bc9", "scanner": "scanner-primary", "fingerprint": "7bb226039f88ecf7", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c2df1ee6cc30b312", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/decks/page.tsx:144"}, "properties": {"repobilityId": "dc2fd5e809956025", "scanner": "scanner-primary", "fingerprint": "c2df1ee6cc30b312", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-eecde1ec269c8936", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/docs/page.tsx:159"}, "properties": {"repobilityId": "a01b35616787aa00", "scanner": "scanner-primary", "fingerprint": "eecde1ec269c8936", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d7243d941b5c9bc9", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:201"}, "properties": {"repobilityId": "a514d3aac31428a6", "scanner": "scanner-primary", "fingerprint": "d7243d941b5c9bc9", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-194ff1c873ad6192", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/page.tsx:147"}, "properties": {"repobilityId": "35229fc84fa6e8ec", "scanner": "scanner-primary", "fingerprint": "194ff1c873ad6192", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6e8e60e185bfc0d6", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/classroom/[cid]/_components/Coursework.tsx:264"}, "properties": {"repobilityId": "eb3f12d4d8eb2d84", "scanner": "scanner-primary", "fingerprint": "6e8e60e185bfc0d6", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b5c8e3a925afcb5c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PermissionGroupsTab.tsx:338"}, "properties": {"repobilityId": "5596a1009c647a7e", "scanner": "scanner-primary", "fingerprint": "b5c8e3a925afcb5c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-36e6622c620c6768", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/permissions/_tabs/PositionTemplatesTab.tsx:375"}, "properties": {"repobilityId": "69b97dfddec5377b", "scanner": "scanner-primary", "fingerprint": "36e6622c620c6768", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c7ab47aa00ed520b", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/page.tsx:127"}, "properties": {"repobilityId": "d33a08ac495104db", "scanner": "scanner-primary", "fingerprint": "c7ab47aa00ed520b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-bfe73052260b5774", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/quiz/[sid]/host/page.tsx:177"}, "properties": {"repobilityId": "be801999eeec76b7", "scanner": "scanner-primary", "fingerprint": "bfe73052260b5774", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-997e49afdea12f06", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/whiteboard/page.tsx:76"}, "properties": {"repobilityId": "dfe613c0b89a7c76", "scanner": "scanner-primary", "fingerprint": "997e49afdea12f06", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d9148246a454e996", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/page.tsx:92"}, "properties": {"repobilityId": "6f85c8d21b94a57c", "scanner": "scanner-primary", "fingerprint": "d9148246a454e996", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d24d3492fb7cca15", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/wordbook/[did]/page.tsx:356"}, "properties": {"repobilityId": "de6de6080329fb94", "scanner": "scanner-primary", "fingerprint": "d24d3492fb7cca15", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-21716d8397908df1", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/page.tsx:161"}, "properties": {"repobilityId": "e3c804f301d6cb98", "scanner": "scanner-primary", "fingerprint": "21716d8397908df1", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-71e6a7bba7f32d42", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/poll/[sid]/host/page.tsx:124"}, "properties": {"repobilityId": "20834733f7ce59a4", "scanner": "scanner-primary", "fingerprint": "71e6a7bba7f32d42", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-48a3914dbbdff79c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/tools/board/page.tsx:70"}, "properties": {"repobilityId": "0a35443ef11385de", "scanner": "scanner-primary", "fingerprint": "48a3914dbbdff79c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d8255a617243d6aa", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(admin)/papers/feed/page.tsx:158"}, "properties": {"repobilityId": "724e99630251cd11", "scanner": "scanner-primary", "fingerprint": "d8255a617243d6aa", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-90a87600b3f32e36", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/sheets/[sid]/page.tsx:79"}, "properties": {"repobilityId": "a24d4c7030fccf64", "scanner": "scanner-primary", "fingerprint": "90a87600b3f32e36", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e57c139b8ec46908", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/decks/[did]/page.tsx:77"}, "properties": {"repobilityId": "04b209fcc147ab27", "scanner": "scanner-primary", "fingerprint": "e57c139b8ec46908", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-151310a0ad54240c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/docs/[did]/page.tsx:65"}, "properties": {"repobilityId": "1c44318321bfeb15", "scanner": "scanner-primary", "fingerprint": "151310a0ad54240c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e09b7c3e15732a3f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/embed/hwps/[hid]/page.tsx:65"}, "properties": {"repobilityId": "bd751c6f2e9b2194", "scanner": "scanner-primary", "fingerprint": "e09b7c3e15732a3f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d19e79a634d0fa89", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/layout.tsx:71"}, "properties": {"repobilityId": "bffa4fc9e79790ea", "scanner": "scanner-primary", "fingerprint": "d19e79a634d0fa89", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1d058a389d5eb9a8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-activities/page.tsx:143"}, "properties": {"repobilityId": "ba53059f3aef9818", "scanner": "scanner-primary", "fingerprint": "1d058a389d5eb9a8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f2e4dc5f660970a0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/docs/page.tsx:157"}, "properties": {"repobilityId": "75c54b15745811dd", "scanner": "scanner-primary", "fingerprint": "f2e4dc5f660970a0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f2ade85a0392ada0", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/dashboard/page.tsx:105"}, "properties": {"repobilityId": "9c9205549c0ecf6a", "scanner": "scanner-primary", "fingerprint": "f2ade85a0392ada0", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6e27722bceec3a78", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/announcements/page.tsx:77"}, "properties": {"repobilityId": "9aab52ea3b466d57", "scanner": "scanner-primary", "fingerprint": "6e27722bceec3a78", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-48367bd5a4dccfa1", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/quiz/[pin]/page.tsx:179"}, "properties": {"repobilityId": "4d8bd6d078c01af1", "scanner": "scanner-primary", "fingerprint": "48367bd5a4dccfa1", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c3619f02b801767f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/past-research/page.tsx:232"}, "properties": {"repobilityId": "b1e0d252217da06a", "scanner": "scanner-primary", "fingerprint": "c3619f02b801767f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9facd18fa9df5099", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.tsx:251"}, "properties": {"repobilityId": "a83c35e58e646372", "scanner": "scanner-primary", "fingerprint": "9facd18fa9df5099", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ff42d1b9bf175e9c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/TimelineTab.tsx:81"}, "properties": {"repobilityId": "546a21af9277c873", "scanner": "scanner-primary", "fingerprint": "ff42d1b9bf175e9c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-da850d3e6b1ff066", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/wordbook/page.tsx:59"}, "properties": {"repobilityId": "c4414f2eeeb0a023", "scanner": "scanner-primary", "fingerprint": "da850d3e6b1ff066", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-ab929d8d0148ea28", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/decks/page.tsx:87"}, "properties": {"repobilityId": "537ddf078070e1e7", "scanner": "scanner-primary", "fingerprint": "ab929d8d0148ea28", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d55e704fa2a8bc48", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/docs/page.tsx:94"}, "properties": {"repobilityId": "9632c9b9a642a32e", "scanner": "scanner-primary", "fingerprint": "d55e704fa2a8bc48", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-33de087ae76b7b17", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/surveys/page.tsx:95"}, "properties": {"repobilityId": "c8a3db21fbf58770", "scanner": "scanner-primary", "fingerprint": "33de087ae76b7b17", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-abdfe796212758f3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/app/(student)/s/classroom/[cid]/_components/StudentCourseworkList.tsx:162"}, "properties": {"repobilityId": "ae89362dc576b71e", "scanner": "scanner-primary", "fingerprint": "abdfe796212758f3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4b8cb5603325086b", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 backend-hocuspocus/src/server.ts:99"}, "properties": {"repobilityId": "af0f223d1c7de601", "scanner": "scanner-primary", "fingerprint": "4b8cb5603325086b", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-f9f4cbbfa0125b40", "level": "warning", "message": {"text": "Privileged port 587 in use"}, "properties": {"repobilityId": "a87a96c3b2e62c08", "scanner": "scanner-primary", "fingerprint": "f9f4cbbfa0125b40", "layer": "network", "severity": "medium", "confidence": 1.0, "tags": ["security", "ports"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/system/SmtpSettings.tsx"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-2fbc1d949b7861fd", "level": "warning", "message": {"text": "Privileged port 465 in use"}, "properties": {"repobilityId": "bcd1f06b60a2b6bc", "scanner": "scanner-primary", "fingerprint": "2fbc1d949b7861fd", "layer": "network", "severity": "medium", "confidence": 1.0, "tags": ["security", "ports"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/system/SmtpSettings.tsx"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-aa5acaa49eb8315b", "level": "note", "message": {"text": "Containers defined but no K8s/orchestration manifest found"}, "properties": {"repobilityId": "b230ea9b68736081", "scanner": "scanner-primary", "fingerprint": "aa5acaa49eb8315b", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["coverage", "deployment"]}}, {"ruleId": "scanner-1094a5c7ee42a291", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in frontend/src/components/hwp/HwpEditor.tsx:131"}, "properties": {"repobilityId": "36efbf7cd221562f", "scanner": "scanner-primary", "fingerprint": "1094a5c7ee42a291", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/hwp/HwpEditor.tsx"}, "region": {"startLine": 131}}}]}, {"ruleId": "scanner-1d474224d13bce39", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/courseware/ProblemContent.tsx:157"}, "properties": {"repobilityId": "4388ff339d1c6386", "scanner": "scanner-primary", "fingerprint": "1d474224d13bce39", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/courseware/ProblemContent.tsx"}, "region": {"startLine": 157}}}]}, {"ruleId": "scanner-8b7aac71ba86c427", "level": "error", "message": {"text": "Insecure pattern 'exec_used' in frontend/src/components/docs/EditorContextMenu.tsx:138"}, "properties": {"repobilityId": "f350ee0c79b23315", "scanner": "scanner-primary", "fingerprint": "8b7aac71ba86c427", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["owasp", "exec_used"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/docs/EditorContextMenu.tsx"}, "region": {"startLine": 138}}}]}, {"ruleId": "scanner-c74e85073d4bd40f", "level": "warning", "message": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/app/auth/register/page.tsx:62"}, "properties": {"repobilityId": "5e5ca5ceb3dbc2a3", "scanner": "scanner-primary", "fingerprint": "c74e85073d4bd40f", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "local_storage_auth_token"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/app/auth/register/page.tsx"}, "region": {"startLine": 62}}}]}, {"ruleId": "scanner-633c813bde13fe1f", "level": "warning", "message": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/auth-context.tsx:146"}, "properties": {"repobilityId": "bfc6764ce70667b2", "scanner": "scanner-primary", "fingerprint": "633c813bde13fe1f", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "local_storage_auth_token"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/lib/auth-context.tsx"}, "region": {"startLine": 146}}}]}, {"ruleId": "scanner-907ff164d2379a68", "level": "warning", "message": {"text": "Insecure pattern 'local_storage_auth_token' in frontend/src/lib/api/client.ts:29"}, "properties": {"repobilityId": "6bdff1662f2f3dae", "scanner": "scanner-primary", "fingerprint": "907ff164d2379a68", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "local_storage_auth_token"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/lib/api/client.ts"}, "region": {"startLine": 29}}}]}, {"ruleId": "scanner-a65ef6dd39dfecee", "level": "note", "message": {"text": "Very large file: frontend/src/components/board/BoardView.tsx (1756 lines)"}, "properties": {"repobilityId": "13629fd87e1d4e78", "scanner": "scanner-primary", "fingerprint": "a65ef6dd39dfecee", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-510b8a489f0dbc29", "level": "note", "message": {"text": "Very large file: frontend/src/app/(admin)/record-writer/[id]/page.tsx (1392 lines)"}, "properties": {"repobilityId": "da82779b941d512d", "scanner": "scanner-primary", "fingerprint": "510b8a489f0dbc29", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-f8de88d795196173", "level": "note", "message": {"text": "Very large file: backend/tests/test_edutools.py (1211 lines)"}, "properties": {"repobilityId": "4f8970c50cd921d7", "scanner": "scanner-primary", "fingerprint": "f8de88d795196173", "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": "f4e344537044293e", "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": "e666c239fe0c3c35", "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": "f97e5a73ce097a77", "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": "bc75289d9a23906b", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-749d4bc1bd66df5f", "level": "warning", "message": {"text": "Agent instructions exist but release-hardening basics are missing"}, "properties": {"repobilityId": "bc1308b864d6aad1", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "d69adb45b78bdd0f", "scanner": "scanner-primary", "fingerprint": "ea8f3013f588db25", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-8424db9c75e04ba4", "level": "none", "message": {"text": "Very short observed git history"}, "properties": {"repobilityId": "1e9f03b59bde4510", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-17d866d91a7c451e", "level": "error", "message": {"text": "Agent instruction/config may expose a secret: CLAUDE.md"}, "properties": {"repobilityId": "ccce5984e58a48f0", "scanner": "scanner-primary", "fingerprint": "17d866d91a7c451e", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["agent-instructions", "secrets", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "CLAUDE.md"}, "region": {"startLine": 351}}}]}, {"ruleId": "scanner-af5de01514bd2a55", "level": "none", "message": {"text": "Commented-code block (5 lines) in frontend/next.config.js:3"}, "properties": {"repobilityId": "28b88d98953e6bf6", "scanner": "scanner-primary", "fingerprint": "af5de01514bd2a55", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-19bc633102e4d476", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/hwp/HwpEditor.tsx:101"}, "properties": {"repobilityId": "6f701c163580183a", "scanner": "scanner-primary", "fingerprint": "19bc633102e4d476", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-91b1685fb278da03", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/drive/DriveBackupActions.tsx:36"}, "properties": {"repobilityId": "14cc1a00da7d698f", "scanner": "scanner-primary", "fingerprint": "91b1685fb278da03", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-cb6d35e1a8176fe7", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step8Supervisors.tsx:30"}, "properties": {"repobilityId": "a65d389ca00bbdf2", "scanner": "scanner-primary", "fingerprint": "cb6d35e1a8176fe7", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d5086187425b4267", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step5Students.tsx:93"}, "properties": {"repobilityId": "0d1c7238f4d5c6d7", "scanner": "scanner-primary", "fingerprint": "d5086187425b4267", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-44cb59a46c5a92ec", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/components/onboarding/steps/Step4Teachers.tsx:121"}, "properties": {"repobilityId": "6ac6f286178648af", "scanner": "scanner-primary", "fingerprint": "44cb59a46c5a92ec", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3b9052e1d2087c85", "level": "note", "message": {"text": "Legacy-named symbol `collab_precaution_seen_v1` in frontend/src/components/collab/CollabPrecautionModal.tsx:25"}, "properties": {"repobilityId": "39d5b20b5e8a5e51", "scanner": "scanner-primary", "fingerprint": "3b9052e1d2087c85", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-15ca9a23993b534f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/users/_components/CsvBulkImportModal.tsx:43"}, "properties": {"repobilityId": "5ab98bafc2d2f324", "scanner": "scanner-primary", "fingerprint": "15ca9a23993b534f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-05288b5658cbef9c", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/page.tsx:114"}, "properties": {"repobilityId": "8b9b757325dafec3", "scanner": "scanner-primary", "fingerprint": "05288b5658cbef9c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-63452e544796e18e", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/students/import/page.tsx:26"}, "properties": {"repobilityId": "38969cd6069b275b", "scanner": "scanner-primary", "fingerprint": "63452e544796e18e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-56493d7e14150275", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/updates/page.tsx:125"}, "properties": {"repobilityId": "84573063b7202acb", "scanner": "scanner-primary", "fingerprint": "56493d7e14150275", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-fc52ee6e0549d0aa", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/enrollments/_components/CsvUploadModal.tsx:51"}, "properties": {"repobilityId": "488075a91bbefc0a", "scanner": "scanner-primary", "fingerprint": "fc52ee6e0549d0aa", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-61f0e44745a2bae4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/semesters/_components/SemesterFormModal.tsx:91"}, "properties": {"repobilityId": "fffe199ad9017d7a", "scanner": "scanner-primary", "fingerprint": "61f0e44745a2bae4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e456e78a060ab369", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/system/research-supervisors/page.tsx:155"}, "properties": {"repobilityId": "39e01a4598842f64", "scanner": "scanner-primary", "fingerprint": "e456e78a060ab369", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-081dd7bbe6b34e42", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(admin)/classroom/[cid]/surveys/[sid]/_components/ResponsesTab.tsx:68"}, "properties": {"repobilityId": "2d503d5a15d8f3f9", "scanner": "scanner-primary", "fingerprint": "081dd7bbe6b34e42", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-5fa6ee0628d6b4ec", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/q/[slug]/page.tsx:38"}, "properties": {"repobilityId": "45616ec7e6b03fbb", "scanner": "scanner-primary", "fingerprint": "5fa6ee0628d6b4ec", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-105671a3459e1706", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 frontend/src/app/(student)/s/my-portfolio/_components/ArtifactsTab.tsx:94"}, "properties": {"repobilityId": "821462644459a2be", "scanner": "scanner-primary", "fingerprint": "105671a3459e1706", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-736772b15bf2070c", "level": "note", "message": {"text": "Legacy-named symbol `test_restore_warns_on_extra_table_in_backup` in backend/tests/test_backup_roundtrip.py:143"}, "properties": {"repobilityId": "97bc718089694115", "scanner": "scanner-primary", "fingerprint": "736772b15bf2070c", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-1d16c4dde649971f", "level": "note", "message": {"text": "Legacy-named symbol `test_v2` in backend/tests/test_filename_normalize.py:175"}, "properties": {"repobilityId": "3002b9c7e3df5355", "scanner": "scanner-primary", "fingerprint": "1d16c4dde649971f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-02e9eb2223159e70", "level": "none", "message": {"text": "Commented-code block (5 lines) in backend/tests/test_security_guards.py:244"}, "properties": {"repobilityId": "d5b4eea8b3112224", "scanner": "scanner-primary", "fingerprint": "02e9eb2223159e70", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-8b7b2a887633fc8f", "level": "note", "message": {"text": "Legacy-named symbol `start_backup` in backend/app/main.py:80"}, "properties": {"repobilityId": "e3ec9b532d5fb665", "scanner": "scanner-primary", "fingerprint": "8b7b2a887633fc8f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-fb058039830ac4fd", "level": "none", "message": {"text": "Commented-code block (5 lines) in backend/app/main.py:107"}, "properties": {"repobilityId": "f5f672d74a20d52a", "scanner": "scanner-primary", "fingerprint": "fb058039830ac4fd", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-9727b27d1d43f962", "level": "none", "message": {"text": "Commented-code block (6 lines) in backend/app/models/classroom.py:161"}, "properties": {"repobilityId": "3b31e7b5e602f391", "scanner": "scanner-primary", "fingerprint": "9727b27d1d43f962", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-bbc6ff9effe473f9", "level": "none", "message": {"text": "Commented-code block (8 lines) in backend/app/models/archive.py:116"}, "properties": {"repobilityId": "7eb0e0395eb11a8b", "scanner": "scanner-primary", "fingerprint": "bbc6ff9effe473f9", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f25e6dda1a449b1a", "level": "none", "message": {"text": "Commented-code block (5 lines) in backend/app/models/user.py:34"}, "properties": {"repobilityId": "d7352e83c356a00c", "scanner": "scanner-primary", "fingerprint": "f25e6dda1a449b1a", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-b8bd6ba7212e31ae", "level": "none", "message": {"text": "Commented-code block (5 lines) in backend/app/modules/drive/organize.py:365"}, "properties": {"repobilityId": "2552488a71e76dcf", "scanner": "scanner-primary", "fingerprint": "b8bd6ba7212e31ae", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-d070ee24917960a4", "level": "note", "message": {"text": "Legacy-named symbol `download_my_drive_backup` in backend/app/modules/drive/backup.py:688"}, "properties": {"repobilityId": "be94c727ea7b4ae1", "scanner": "scanner-primary", "fingerprint": "d070ee24917960a4", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-29f9b5e6b364ccac", "level": "none", "message": {"text": "Commented-code block (6 lines) in backend/app/modules/club/router.py:198"}, "properties": {"repobilityId": "df441a6b1a0e442c", "scanner": "scanner-primary", "fingerprint": "29f9b5e6b364ccac", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-8c0d3ac97959c8b4", "level": "note", "message": {"text": "Legacy-named symbol `export_backup` in backend/app/modules/system/backup.py:33"}, "properties": {"repobilityId": "34d1b3d63d606191", "scanner": "scanner-primary", "fingerprint": "8c0d3ac97959c8b4", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-c4277284f5366a0a", "level": "note", "message": {"text": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/router.py:731"}, "properties": {"repobilityId": "a9cdd282f64f48f6", "scanner": "scanner-primary", "fingerprint": "c4277284f5366a0a", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-bb594bd17240f7b2", "level": "none", "message": {"text": "Commented-code block (7 lines) in backend/app/modules/classroom/schemas.py:15"}, "properties": {"repobilityId": "d06fd4e9acc125fa", "scanner": "scanner-primary", "fingerprint": "bb594bd17240f7b2", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-75b86d55bc9472bd", "level": "note", "message": {"text": "Legacy-named symbol `student_copy` in backend/app/modules/classroom/student_copy.py:15"}, "properties": {"repobilityId": "6f3bbee7b7bdfc1c", "scanner": "scanner-primary", "fingerprint": "75b86d55bc9472bd", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-6c2b36cc0fa19706", "level": "warning", "message": {"text": "Network/subprocess call without timeout or try/except \u2014 backend/app/modules/ai_developer/service.py:481"}, "properties": {"repobilityId": "99291ca144b3de0d", "scanner": "scanner-primary", "fingerprint": "6c2b36cc0fa19706", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e631eb17aa658cd6", "level": "note", "message": {"text": "Legacy-named symbol `student_copy` in backend/app/services/attachment_share.py:8"}, "properties": {"repobilityId": "fd2bc0b6215447f2", "scanner": "scanner-primary", "fingerprint": "e631eb17aa658cd6", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-ab99e6037bd650de", "level": "note", "message": {"text": "Legacy-named symbol `deleted_old` in backend/app/core/backup_scheduler.py:238"}, "properties": {"repobilityId": "8b33217aef416b92", "scanner": "scanner-primary", "fingerprint": "ab99e6037bd650de", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-ee6f2d3b20bd1c82", "level": "none", "message": {"text": "Commented-code block (7 lines) in backend/app/core/config.py:99"}, "properties": {"repobilityId": "c71a52bab6485016", "scanner": "scanner-primary", "fingerprint": "ee6f2d3b20bd1c82", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-371280905d3f03ea", "level": "note", "message": {"text": "Legacy-named symbol `truly_unused` in backend/app/core/permission_registry.py:94"}, "properties": {"repobilityId": "9b7564c6ba3186b8", "scanner": "scanner-primary", "fingerprint": "371280905d3f03ea", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-5bf3f52a5a5d6967", "level": "none", "message": {"text": "Commented-code block (6 lines) in backend/app/core/files.py:127"}, "properties": {"repobilityId": "1e9b78844ee8e0af", "scanner": "scanner-primary", "fingerprint": "5bf3f52a5a5d6967", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-9e8da0591b6fca89", "level": "none", "message": {"text": "Commented-code block (6 lines) in backend/app/core/permissions.py:160"}, "properties": {"repobilityId": "8d45b512460843b1", "scanner": "scanner-primary", "fingerprint": "9e8da0591b6fca89", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-c331f928552f5d43", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/auth.ts:94"}, "properties": {"repobilityId": "e74d7c985d13323d", "scanner": "scanner-primary", "fingerprint": "c331f928552f5d43", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-8743ea4b971a9d43", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 backend-hocuspocus/src/storage.ts:17"}, "properties": {"repobilityId": "a8d070d544bf6e5e", "scanner": "scanner-primary", "fingerprint": "8743ea4b971a9d43", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-022e6399d1ef2beb", "level": "note", "message": {"text": "12 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "7bf4178327da75a2", "scanner": "scanner-primary", "fingerprint": "022e6399d1ef2beb", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "80b5aab6154bf9b8", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "bfbbd68a15b9814c", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "cd0751d455410033", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "9c001f7614415972", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "7e08b3a5b452aedb", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "818b68d8d204cdac", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "50b2519a163d1139", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "580264ec02421285", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "28e966ee6cb63eae", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "f8fd9ee13fd260d9", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "7eae0e942ead1c6f", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-49c98f7cedd9c977", "level": "note", "message": {"text": "Near-duplicate function bodies in 4 places"}, "properties": {"repobilityId": "2368d66339852618", "scanner": "scanner-primary", "fingerprint": "49c98f7cedd9c977", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "99490c2a1099810f", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "13f7c24ee99d8e63", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "995c91ae29710943", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "348fea8239f48b6b", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "927b88aeef4cf914", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "82af12288ab84db4", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "049430dd0f34d090", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "41800ccf5379a593", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-8bc0a6c3e8884516", "level": "error", "message": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_whiteboard/router.py:420"}, "properties": {"repobilityId": "b8d409fb404795d9", "scanner": "scanner-primary", "fingerprint": "8bc0a6c3e8884516", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/tool_whiteboard/router.py"}, "region": {"startLine": 420}}}]}, {"ruleId": "scanner-b18243180feabc0f", "level": "error", "message": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_sheets/router.py:478"}, "properties": {"repobilityId": "889c23627a7c6cb0", "scanner": "scanner-primary", "fingerprint": "b18243180feabc0f", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/classroom_sheets/router.py"}, "region": {"startLine": 478}}}]}, {"ruleId": "scanner-02dfc83a70eed161", "level": "error", "message": {"text": "FastAPI POST `refresh` without auth dependency \u2014 backend/app/modules/auth/session.py:30"}, "properties": {"repobilityId": "e072e8844c5d73c5", "scanner": "scanner-primary", "fingerprint": "02dfc83a70eed161", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/auth/session.py"}, "region": {"startLine": 30}}}]}, {"ruleId": "scanner-d37dbf4b184ee65c", "level": "error", "message": {"text": "FastAPI POST `verify_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:224"}, "properties": {"repobilityId": "53e8fc716b22cb6d", "scanner": "scanner-primary", "fingerprint": "d37dbf4b184ee65c", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/auth/login_flow.py"}, "region": {"startLine": 224}}}]}, {"ruleId": "scanner-611bc4c337985e38", "level": "error", "message": {"text": "FastAPI POST `resend_email_code` without auth dependency \u2014 backend/app/modules/auth/login_flow.py:307"}, "properties": {"repobilityId": "433123b251698d58", "scanner": "scanner-primary", "fingerprint": "611bc4c337985e38", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/auth/login_flow.py"}, "region": {"startLine": 307}}}]}, {"ruleId": "scanner-d78057145ab2d40d", "level": "error", "message": {"text": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_slides/hocuspocus.py:63"}, "properties": {"repobilityId": "a0e2fc131e0af988", "scanner": "scanner-primary", "fingerprint": "d78057145ab2d40d", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/classroom_slides/hocuspocus.py"}, "region": {"startLine": 63}}}]}, {"ruleId": "scanner-8401645f4c5e4e46", "level": "error", "message": {"text": "FastAPI POST `put_yjs_snapshot` without auth dependency \u2014 backend/app/modules/tool_board/router.py:565"}, "properties": {"repobilityId": "5eb94b7a62af59d2", "scanner": "scanner-primary", "fingerprint": "8401645f4c5e4e46", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/tool_board/router.py"}, "region": {"startLine": 565}}}]}, {"ruleId": "scanner-7571ac0e003f495b", "level": "error", "message": {"text": "FastAPI POST `save_yjs_snapshot` without auth dependency \u2014 backend/app/modules/classroom_docs/hocuspocus.py:71"}, "properties": {"repobilityId": "10e8d590f9d51eee", "scanner": "scanner-primary", "fingerprint": "7571ac0e003f495b", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/modules/classroom_docs/hocuspocus.py"}, "region": {"startLine": 71}}}]}, {"ruleId": "scanner-e6e2883e32144bb3", "level": "error", "message": {"text": "Dangling fetch: POST /api/students/_io/import/${type}?dry_run=${dryRun} (frontend/src/app/(admin)/students/import/page.tsx:42)"}, "properties": {"repobilityId": "685f1dad1fb23e73", "scanner": "scanner-primary", "fingerprint": "e6e2883e32144bb3", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f7108c6e2f86a988", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/feedback/${id} (frontend/src/app/(admin)/system/feedback/page.tsx:57)"}, "properties": {"repobilityId": "c5ae3f684cad36fd", "scanner": "scanner-primary", "fingerprint": "f7108c6e2f86a988", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-4756b4c4da7d2088", "level": "note", "message": {"text": "Unused endpoint: GET /api/health"}, "properties": {"repobilityId": "414de3a0f9ad89dd", "scanner": "scanner-primary", "fingerprint": "4756b4c4da7d2088", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5baa8971ebe192a1", "level": "note", "message": {"text": "Unused endpoint: GET /"}, "properties": {"repobilityId": "33e6d6166092e50b", "scanner": "scanner-primary", "fingerprint": "5baa8971ebe192a1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7a009b1a56794f45", "level": "note", "message": {"text": "Unused endpoint: POST /"}, "properties": {"repobilityId": "12c03493a64ab665", "scanner": "scanner-primary", "fingerprint": "7a009b1a56794f45", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-84e8012733260152", "level": "note", "message": {"text": "Unused endpoint: PUT /{wid}"}, "properties": {"repobilityId": "9a39e35515d114bd", "scanner": "scanner-primary", "fingerprint": "84e8012733260152", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f2affefa676c9ef2", "level": "note", "message": {"text": "Unused endpoint: DELETE /{wid}"}, "properties": {"repobilityId": "25a31672c0e1c597", "scanner": "scanner-primary", "fingerprint": "f2affefa676c9ef2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-c2df71d5e1393d57", "level": "note", "message": {"text": "Unused endpoint: GET /shared-with-me"}, "properties": {"repobilityId": "f28515c13858e364", "scanner": "scanner-primary", "fingerprint": "c2df71d5e1393d57", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4b46fcd7564beb19", "level": "note", "message": {"text": "Unused endpoint: GET /{wid}/shares"}, "properties": {"repobilityId": "ba46a7af162d7762", "scanner": "scanner-primary", "fingerprint": "4b46fcd7564beb19", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-77a8c3fa0a83b3ef", "level": "note", "message": {"text": "Unused endpoint: POST /{wid}/shares"}, "properties": {"repobilityId": "379f757a196319b3", "scanner": "scanner-primary", "fingerprint": "77a8c3fa0a83b3ef", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-042262e1565065fa", "level": "note", "message": {"text": "Unused endpoint: DELETE /{wid}/shares/{share_id}"}, "properties": {"repobilityId": "503b418aa5c89e0d", "scanner": "scanner-primary", "fingerprint": "042262e1565065fa", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-741ca724c77e3a50", "level": "note", "message": {"text": "Unused endpoint: POST /{wid}/duplicate"}, "properties": {"repobilityId": "c8eab96fb8d6a8d0", "scanner": "scanner-primary", "fingerprint": "741ca724c77e3a50", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4fbbb30e149acbe0", "level": "note", "message": {"text": "Unused endpoint: GET /{wid}"}, "properties": {"repobilityId": "b51e9e7daaa11a61", "scanner": "scanner-primary", "fingerprint": "4fbbb30e149acbe0", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-69d26da2b41e20dd", "level": "note", "message": {"text": "Unused endpoint: GET /{wid}/permission"}, "properties": {"repobilityId": "cd825d61b25cc331", "scanner": "scanner-primary", "fingerprint": "69d26da2b41e20dd", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-35b8d1979c5bca07", "level": "note", "message": {"text": "Unused endpoint: GET /{wid}/yjs-snapshot"}, "properties": {"repobilityId": "603a259c801367bc", "scanner": "scanner-primary", "fingerprint": "35b8d1979c5bca07", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7250c435597fbc88", "level": "note", "message": {"text": "Unused endpoint: POST /{wid}/yjs-snapshot"}, "properties": {"repobilityId": "8d71c6c7ba479f7d", "scanner": "scanner-primary", "fingerprint": "7250c435597fbc88", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-56e3411959f4c471", "level": "note", "message": {"text": "Unused endpoint: GET /{sid}"}, "properties": {"repobilityId": "5a3e86e1505d47b2", "scanner": "scanner-primary", "fingerprint": "56e3411959f4c471", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9c138d64a0584bde", "level": "note", "message": {"text": "Unused endpoint: PUT /{sid}"}, "properties": {"repobilityId": "51e09737ddb974cd", "scanner": "scanner-primary", "fingerprint": "9c138d64a0584bde", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f0ec7c44529c217a", "level": "note", "message": {"text": "Unused endpoint: DELETE /{sid}"}, "properties": {"repobilityId": "c263e9b0fab47383", "scanner": "scanner-primary", "fingerprint": "f0ec7c44529c217a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-df46e72dad75b2ac", "level": "note", "message": {"text": "Unused endpoint: GET /{sid}/members"}, "properties": {"repobilityId": "89a764b27cad34d3", "scanner": "scanner-primary", "fingerprint": "df46e72dad75b2ac", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b34280d434a4c879", "level": "note", "message": {"text": "Unused endpoint: POST /{sid}/members"}, "properties": {"repobilityId": "751e6d5d44cdf657", "scanner": "scanner-primary", "fingerprint": "b34280d434a4c879", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5fe781793fc8a768", "level": "note", "message": {"text": "Unused endpoint: DELETE /{sid}/members/{mid}"}, "properties": {"repobilityId": "acca2ff55118a3ad", "scanner": "scanner-primary", "fingerprint": "5fe781793fc8a768", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0f5bee8a91c7102d", "level": "note", "message": {"text": "Unused endpoint: GET /{sid}/snapshot-state"}, "properties": {"repobilityId": "5f4ac712924d471e", "scanner": "scanner-primary", "fingerprint": "0f5bee8a91c7102d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1681087120e2a279", "level": "note", "message": {"text": "Unused endpoint: POST /{sid}/snapshot-state"}, "properties": {"repobilityId": "e88fc4eeb5666358", "scanner": "scanner-primary", "fingerprint": "1681087120e2a279", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-93cc38716dc089e1", "level": "note", "message": {"text": "Unused endpoint: GET /{sid}/permission"}, "properties": {"repobilityId": "13ef2a530f54ece0", "scanner": "scanner-primary", "fingerprint": "93cc38716dc089e1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0e20b7af493c8f0d", "level": "note", "message": {"text": "Unused endpoint: GET /{sid}/yjs-snapshot"}, "properties": {"repobilityId": "1fa6a1256c77bca8", "scanner": "scanner-primary", "fingerprint": "0e20b7af493c8f0d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-35c35b01eed84801", "level": "note", "message": {"text": "Unused endpoint: POST /{sid}/yjs-snapshot"}, "properties": {"repobilityId": "5ffefc3679dec6f8", "scanner": "scanner-primary", "fingerprint": "35c35b01eed84801", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-2e64ea31216de42a", "level": "note", "message": {"text": "Unused endpoint: POST /_from-survey/{survey_id}"}, "properties": {"repobilityId": "531b411804544270", "scanner": "scanner-primary", "fingerprint": "2e64ea31216de42a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5f802dd74f719e97", "level": "note", "message": {"text": "Unused endpoint: GET /_survey-data/{survey_id}"}, "properties": {"repobilityId": "0897e6ca05a4e0e7", "scanner": "scanner-primary", "fingerprint": "5f802dd74f719e97", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b15d518d538de57d", "level": "note", "message": {"text": "Unused endpoint: GET /sessions"}, "properties": {"repobilityId": "f4ee641bc74680e5", "scanner": "scanner-primary", "fingerprint": "b15d518d538de57d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-77e497414a27bc4a", "level": "note", "message": {"text": "Unused endpoint: GET /sessions/{sid}"}, "properties": {"repobilityId": "facba89c632691b1", "scanner": "scanner-primary", "fingerprint": "77e497414a27bc4a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-2d50d060a6759dd1", "level": "note", "message": {"text": "Unused endpoint: POST /sessions/{sid}/start"}, "properties": {"repobilityId": "4173b47c5212569a", "scanner": "scanner-primary", "fingerprint": "2d50d060a6759dd1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-58eb99430326c227", "level": "note", "message": {"text": "Unused endpoint: POST /sessions/{sid}/goto"}, "properties": {"repobilityId": "3e964d124ca316b5", "scanner": "scanner-primary", "fingerprint": "58eb99430326c227", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-aaa82271e440d1e3", "level": "note", "message": {"text": "Unused endpoint: POST /sessions/{sid}/end"}, "properties": {"repobilityId": "ee3a7aaeb487879c", "scanner": "scanner-primary", "fingerprint": "aaa82271e440d1e3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-79437adc194451bb", "level": "note", "message": {"text": "Unused endpoint: GET /sessions/{sid}/qr.png"}, "properties": {"repobilityId": "670c824ebd898e1d", "scanner": "scanner-primary", "fingerprint": "79437adc194451bb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-633d0a653ebbf1bf", "level": "note", "message": {"text": "Unused endpoint: POST /join"}, "properties": {"repobilityId": "dddb46561ca0a082", "scanner": "scanner-primary", "fingerprint": "633d0a653ebbf1bf", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-26d7690fd478981c", "level": "note", "message": {"text": "Unused endpoint: GET /play/{sid}/state"}, "properties": {"repobilityId": "8611c217bf7214c0", "scanner": "scanner-primary", "fingerprint": "26d7690fd478981c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-ec65dd7db4438f8e", "level": "note", "message": {"text": "Unused endpoint: POST /play/{sid}/respond"}, "properties": {"repobilityId": "2e2682ac2295c0d3", "scanner": "scanner-primary", "fingerprint": "ec65dd7db4438f8e", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-6b7100fe091a61c3", "level": "note", "message": {"text": "Unused endpoint: GET /{pid}"}, "properties": {"repobilityId": "95114582af458f8d", "scanner": "scanner-primary", "fingerprint": "6b7100fe091a61c3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-d25a3163609d8afb", "level": "note", "message": {"text": "Unused endpoint: PUT /{pid}"}, "properties": {"repobilityId": "ccf5b4f5594ffe55", "scanner": "scanner-primary", "fingerprint": "d25a3163609d8afb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f2c97ea12d1c0b2b", "level": "note", "message": {"text": "Unused endpoint: DELETE /{pid}"}, "properties": {"repobilityId": "a0344fe2115507e1", "scanner": "scanner-primary", "fingerprint": "f2c97ea12d1c0b2b", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4b640ecf415b7200", "level": "note", "message": {"text": "Unused endpoint: POST /{pid}/sessions"}, "properties": {"repobilityId": "278029d182403207", "scanner": "scanner-primary", "fingerprint": "4b640ecf415b7200", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-ac3f3e641cad22ed", "level": "note", "message": {"text": "Unused endpoint: POST /_quota/bulk"}, "properties": {"repobilityId": "b06e1d4f27578414", "scanner": "scanner-primary", "fingerprint": "ac3f3e641cad22ed", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f910390613da27ca", "level": "note", "message": {"text": "Unused endpoint: POST /{user_id}/quota"}, "properties": {"repobilityId": "e724091e4ff1cf50", "scanner": "scanner-primary", "fingerprint": "f910390613da27ca", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-bef922eb7c9f3ceb", "level": "note", "message": {"text": "Unused endpoint: PATCH /{user_id}/lifecycle"}, "properties": {"repobilityId": "31ef10f843364272", "scanner": "scanner-primary", "fingerprint": "bef922eb7c9f3ceb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-a50e09dd0a98cec7", "level": "note", "message": {"text": "Unused endpoint: POST /{user_id}/transfer-ownership"}, "properties": {"repobilityId": "2f87d53f313beda7", "scanner": "scanner-primary", "fingerprint": "a50e09dd0a98cec7", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-c368dede2e2f5cee", "level": "note", "message": {"text": "Unused endpoint: GET /peers"}, "properties": {"repobilityId": "8a9a88db99c70b98", "scanner": "scanner-primary", "fingerprint": "c368dede2e2f5cee", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-3cdc30f2b9308e3f", "level": "note", "message": {"text": "Unused endpoint: PUT /{user_id}"}, "properties": {"repobilityId": "a2bcecd260fd0a29", "scanner": "scanner-primary", "fingerprint": "3cdc30f2b9308e3f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-55c93ee7e031a509", "level": "note", "message": {"text": "Unused endpoint: DELETE /{user_id}"}, "properties": {"repobilityId": "6ec1599f46786593", "scanner": "scanner-primary", "fingerprint": "55c93ee7e031a509", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f2e38a416ddc468f", "level": "note", "message": {"text": "Unused endpoint: GET /excel-template"}, "properties": {"repobilityId": "0a8e51d54ff6da2d", "scanner": "scanner-primary", "fingerprint": "f2e38a416ddc468f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-ebd27a62b6856686", "level": "note", "message": {"text": "Unused endpoint: POST /bulk-import/validate"}, "properties": {"repobilityId": "dac66c61f77b3b9e", "scanner": "scanner-primary", "fingerprint": "ebd27a62b6856686", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5b522763d542fab7", "level": "note", "message": {"text": "Unused endpoint: POST /bulk-import/confirm"}, "properties": {"repobilityId": "5e8148110cacc034", "scanner": "scanner-primary", "fingerprint": "5b522763d542fab7", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}