{"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-03ce2999be0d5625", "name": "Possibly dead Python function: consolidate_user", "shortDescription": {"text": "Possibly dead Python function: consolidate_user"}, "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-972635630c4cf563", "name": "Possibly dead Python function: require_admin", "shortDescription": {"text": "Possibly dead Python function: require_admin"}, "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-8f8212c7a8211dc1", "name": "Possibly dead Python function: vision_extract_page", "shortDescription": {"text": "Possibly dead Python function: vision_extract_page"}, "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-94ee9e645edf3aa0", "name": "Possibly dead Python function: call", "shortDescription": {"text": "Possibly dead Python function: call"}, "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-bf96c4b03a100741", "name": "Possibly dead Python function: count_recent_uploads", "shortDescription": {"text": "Possibly dead Python function: count_recent_uploads"}, "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-5b54bb19b1e9f5b8", "name": "Possibly dead Python function: prog", "shortDescription": {"text": "Possibly dead Python function: prog"}, "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-7b2613a7aaedcd12", "name": "Possibly dead Python function: save_upload_pdf", "shortDescription": {"text": "Possibly dead Python function: save_upload_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-59f20d6f470c62bd", "name": "Possibly dead Python function: send_with_headers", "shortDescription": {"text": "Possibly dead Python function: send_with_headers"}, "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-976ba2ae971393fb", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/Markdown.tsx:12", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/Markdown.tsx:12"}, "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-43bced65a560106c", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StatsPage.tsx:142", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StatsPage.tsx:142"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c0acfdda8bcfdb96", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/SettingsPage.tsx:270", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/SettingsPage.tsx:270"}, "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-7ee2296ba4f4d2c6", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/DashboardPage.tsx:284", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/DashboardPage.tsx:284"}, "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-acafe3b1a5f97dde", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StudentsPage.tsx:61", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StudentsPage.tsx:61"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-48772175319e1c2a", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/JobPage.tsx:160", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/JobPage.tsx:160"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-6b58b9a7dc50056a", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PapersPage.tsx:131", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PapersPage.tsx:131"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-d74bd91c10b7bf70", "name": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PaperPage.tsx:218", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PaperPage.tsx:218"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-14ee8745e4c03762", "name": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/Markdown.tsx:12", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/Markdown.tsx:12"}, "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-6429fdd60ee1ea1a", "name": "GitHub Action is tag-pinned rather than SHA-pinned", "shortDescription": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "fullDescription": {"text": "softprops/action-gh-release@v2 can move without a code change in this repo. Pin third-party actions to a reviewed 40-character commit SHA."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-9995793ac152fd7c", "name": "GitHub Actions workflow grants broad write permissions", "shortDescription": {"text": "GitHub Actions workflow grants broad write permissions"}, "fullDescription": {"text": "CI tokens with write permissions increase blast radius when an action, dependency, or PR workflow is compromised. Prefer job-level least-privilege permissions."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6893a6c8b0861585", "name": "Very low test-to-source ratio", "shortDescription": {"text": "Very low test-to-source ratio"}, "fullDescription": {"text": "0 test file(s) for 60 source file(s) (ratio 0.00). 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 9 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 14 placeholder/mock markers across 8 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-11825279136b53a3", "name": "CI is configured but no tests are detected", "shortDescription": {"text": "CI is configured but no tests are detected"}, "fullDescription": {"text": "A CI pipeline exists, but the scan found no test files to gate. Opus labeled this generated-code pattern as config theater: release machinery exists, but it has little behavioral signal."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b9088664ace7f748", "name": "Composite production-readiness gap", "shortDescription": {"text": "Composite production-readiness gap"}, "fullDescription": {"text": "Multiple low-cost hardening controls are missing together: license, tests. Opus verification showed these co-occurring gaps are a better readiness signal than reading each flag in isolation."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-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-1b5a76780c3df9aa", "name": "1 env vars used in code but missing from .env.example", "shortDescription": {"text": "1 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `EXAMTUTOR_DATA_DIR`. Add them (with a placeholder/comment) to .env.example so onboarding doesn't break."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-be46ea126aa5d8dc", "name": "Near-duplicate function bodies in 3 places", "shortDescription": {"text": "Near-duplicate function bodies in 3 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\nbackend/app/llm.py:call, backend/app/llm.py:call, backend/app/llm.py:call\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-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/store.py:get_user_by_username, backend/app/store.py:get_user\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-0f7f405e7897f3ad", "name": "Frontend route `/download` has no Link/navigate to it \u2014 frontend/src/App.tsx", "shortDescription": {"text": "Frontend route `/download` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-3017f1fe0af24f3a", "name": "Frontend route `/jobs/:jobId` has no Link/navigate to it \u2014 frontend/src/App.tsx", "shortDescription": {"text": "Frontend route `/jobs/:jobId` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-9ffbd37caebcd744", "name": "Frontend route `/papers/:paperId` has no Link/navigate to it \u2014 frontend/src/App.tsx", "shortDescription": {"text": "Frontend route `/papers/:paperId` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b52594fc05d6aea6", "name": "Dangling fetch: GET /api/auth/me (frontend/src/api.ts:77)", "shortDescription": {"text": "Dangling fetch: GET /api/auth/me (frontend/src/api.ts:77)"}, "fullDescription": {"text": "`frontend/src/api.ts:77` calls `GET /api/auth/me` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/auth/me`\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-5384d1b4ec898e2f", "name": "Dangling fetch: POST /api/auth/login (frontend/src/api.ts:79)", "shortDescription": {"text": "Dangling fetch: POST /api/auth/login (frontend/src/api.ts:79)"}, "fullDescription": {"text": "`frontend/src/api.ts:79` calls `POST /api/auth/login` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/auth/login`\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-91507804acf89d19", "name": "Dangling fetch: POST /api/auth/register (frontend/src/api.ts:81)", "shortDescription": {"text": "Dangling fetch: POST /api/auth/register (frontend/src/api.ts:81)"}, "fullDescription": {"text": "`frontend/src/api.ts:81` calls `POST /api/auth/register` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/auth/register`\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-2a030bceeb99762b", "name": "Dangling fetch: POST /api/auth/logout (frontend/src/api.ts:85)", "shortDescription": {"text": "Dangling fetch: POST /api/auth/logout (frontend/src/api.ts:85)"}, "fullDescription": {"text": "`frontend/src/api.ts:85` calls `POST /api/auth/logout` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/auth/logout`\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-de0dba035f953b47", "name": "Dangling fetch: POST /api/auth/password (frontend/src/api.ts:87)", "shortDescription": {"text": "Dangling fetch: POST /api/auth/password (frontend/src/api.ts:87)"}, "fullDescription": {"text": "`frontend/src/api.ts:87` calls `POST /api/auth/password` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/auth/password`\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-c3f9d6c5d624b84a", "name": "Dangling fetch: POST /api/admin/invites (frontend/src/api.ts:92)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/invites (frontend/src/api.ts:92)"}, "fullDescription": {"text": "`frontend/src/api.ts:92` calls `POST /api/admin/invites` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/invites`\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-7af6d06d83466f22", "name": "Dangling fetch: GET /api/admin/invites (frontend/src/api.ts:96)", "shortDescription": {"text": "Dangling fetch: GET /api/admin/invites (frontend/src/api.ts:96)"}, "fullDescription": {"text": "`frontend/src/api.ts:96` calls `GET /api/admin/invites` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/invites`\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-204c22e0d62391db", "name": "Dangling fetch: DELETE /api/admin/invites/${encodeURIComponent(code)} (frontend/src/api.ts:98)", "shortDescription": {"text": "Dangling fetch: DELETE /api/admin/invites/${encodeURIComponent(code)} (frontend/src/api.ts:98)"}, "fullDescription": {"text": "`frontend/src/api.ts:98` calls `DELETE /api/admin/invites/${encodeURIComponent(code)}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/invites/<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-3478a20d2d2c0543", "name": "Dangling fetch: GET /api/students (frontend/src/api.ts:103)", "shortDescription": {"text": "Dangling fetch: GET /api/students (frontend/src/api.ts:103)"}, "fullDescription": {"text": "`frontend/src/api.ts:103` calls `GET /api/students` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/students`\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-4e8869e8f0c1ccad", "name": "Dangling fetch: POST /api/students (frontend/src/api.ts:105)", "shortDescription": {"text": "Dangling fetch: POST /api/students (frontend/src/api.ts:105)"}, "fullDescription": {"text": "`frontend/src/api.ts:105` calls `POST /api/students` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/students`\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-7de7a3bce5a32b58", "name": "Dangling fetch: PATCH /api/students/${id} (frontend/src/api.ts:107)", "shortDescription": {"text": "Dangling fetch: PATCH /api/students/${id} (frontend/src/api.ts:107)"}, "fullDescription": {"text": "`frontend/src/api.ts:107` calls `PATCH /api/students/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/students/<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-714527a3e97a51a1", "name": "Dangling fetch: DELETE /api/students/${id} (frontend/src/api.ts:109)", "shortDescription": {"text": "Dangling fetch: DELETE /api/students/${id} (frontend/src/api.ts:109)"}, "fullDescription": {"text": "`frontend/src/api.ts:109` calls `DELETE /api/students/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/students/<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-fa6f95d7daef08a4", "name": "Dangling fetch: GET /api/papers (frontend/src/api.ts:112)", "shortDescription": {"text": "Dangling fetch: GET /api/papers (frontend/src/api.ts:112)"}, "fullDescription": {"text": "`frontend/src/api.ts:112` calls `GET /api/papers` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers`\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-b8c454057d21fa28", "name": "Dangling fetch: GET /api/papers/${id} (frontend/src/api.ts:113)", "shortDescription": {"text": "Dangling fetch: GET /api/papers/${id} (frontend/src/api.ts:113)"}, "fullDescription": {"text": "`frontend/src/api.ts:113` calls `GET /api/papers/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<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-a97b9d0b6f120403", "name": "Dangling fetch: PATCH /api/papers/${id} (frontend/src/api.ts:132)", "shortDescription": {"text": "Dangling fetch: PATCH /api/papers/${id} (frontend/src/api.ts:132)"}, "fullDescription": {"text": "`frontend/src/api.ts:132` calls `PATCH /api/papers/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<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-3bf8befca599a823", "name": "Dangling fetch: POST /api/papers/manual (frontend/src/api.ts:134)", "shortDescription": {"text": "Dangling fetch: POST /api/papers/manual (frontend/src/api.ts:134)"}, "fullDescription": {"text": "`frontend/src/api.ts:134` calls `POST /api/papers/manual` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/manual`\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-64be6f3334933d87", "name": "Dangling fetch: POST /api/papers/${id}/questions (frontend/src/api.ts:139)", "shortDescription": {"text": "Dangling fetch: POST /api/papers/${id}/questions (frontend/src/api.ts:139)"}, "fullDescription": {"text": "`frontend/src/api.ts:139` calls `POST /api/papers/${id}/questions` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<p>/questions`\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-93e9ee98f3583320", "name": "Dangling fetch: DELETE /api/papers/${id}/questions/${qid} (frontend/src/api.ts:144)", "shortDescription": {"text": "Dangling fetch: DELETE /api/papers/${id}/questions/${qid} (frontend/src/api.ts:144)"}, "fullDescription": {"text": "`frontend/src/api.ts:144` calls `DELETE /api/papers/${id}/questions/${qid}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<p>/questions/<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-88caeec8f85ab092", "name": "Dangling fetch: DELETE /api/papers/${id} (frontend/src/api.ts:146)", "shortDescription": {"text": "Dangling fetch: DELETE /api/papers/${id} (frontend/src/api.ts:146)"}, "fullDescription": {"text": "`frontend/src/api.ts:146` calls `DELETE /api/papers/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<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-88e374a8b5d8cec1", "name": "Dangling fetch: POST /api/papers/${id}/reprocess (frontend/src/api.ts:148)", "shortDescription": {"text": "Dangling fetch: POST /api/papers/${id}/reprocess (frontend/src/api.ts:148)"}, "fullDescription": {"text": "`frontend/src/api.ts:148` calls `POST /api/papers/${id}/reprocess` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<p>/reprocess`\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-c52c78e1b368488e", "name": "Dangling fetch: PATCH /api/papers/${id}/questions/${qid} (frontend/src/api.ts:150)", "shortDescription": {"text": "Dangling fetch: PATCH /api/papers/${id}/questions/${qid} (frontend/src/api.ts:150)"}, "fullDescription": {"text": "`frontend/src/api.ts:150` calls `PATCH /api/papers/${id}/questions/${qid}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/papers/<p>/questions/<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-1162d3e9be0b2b5d", "name": "Dangling fetch: POST /api/jobs/${jobId}/questions/${qid}/override (frontend/src/api.ts:187)", "shortDescription": {"text": "Dangling fetch: POST /api/jobs/${jobId}/questions/${qid}/override (frontend/src/api.ts:187)"}, "fullDescription": {"text": "`frontend/src/api.ts:187` calls `POST /api/jobs/${jobId}/questions/${qid}/override` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/jobs/<p>/questions/<p>/override`\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-deffa868025db22e", "name": "Dangling fetch: POST /api/jobs/${jobId}/ask (frontend/src/api.ts:199)", "shortDescription": {"text": "Dangling fetch: POST /api/jobs/${jobId}/ask (frontend/src/api.ts:199)"}, "fullDescription": {"text": "`frontend/src/api.ts:199` calls `POST /api/jobs/${jobId}/ask` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/jobs/<p>/ask`\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-3badeb1a0993aebc", "name": "Dangling fetch: GET /api/releases (frontend/src/api.ts:219)", "shortDescription": {"text": "Dangling fetch: GET /api/releases (frontend/src/api.ts:219)"}, "fullDescription": {"text": "`frontend/src/api.ts:219` calls `GET /api/releases` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/releases`\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-91786ec9e40e27ca", "name": "Unused endpoint: GET /{path:path}", "shortDescription": {"text": "Unused endpoint: GET /{path:path}"}, "fullDescription": {"text": "`backend/app/main.py` declares `GET /{path:path}` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7a009b1a56794f45", "name": "Unused endpoint: POST /", "shortDescription": {"text": "Unused endpoint: POST /"}, "fullDescription": {"text": "`backend/app/routers/papers.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-af6608de4c7fbf85", "name": "Unused endpoint: POST /manual", "shortDescription": {"text": "Unused endpoint: POST /manual"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `POST /manual` 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-7e441539d2fdcd6d", "name": "Unused endpoint: POST /{paper_id}/files", "shortDescription": {"text": "Unused endpoint: POST /{paper_id}/files"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `POST /{paper_id}/files` 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/routers/papers.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-7a837f93f9c1d3ba", "name": "Unused endpoint: GET /{paper_id}", "shortDescription": {"text": "Unused endpoint: GET /{paper_id}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `GET /{paper_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-282fd09ae0dc7867", "name": "Unused endpoint: PATCH /{paper_id}", "shortDescription": {"text": "Unused endpoint: PATCH /{paper_id}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `PATCH /{paper_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-91941e41734a4fb4", "name": "Unused endpoint: DELETE /{paper_id}", "shortDescription": {"text": "Unused endpoint: DELETE /{paper_id}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `DELETE /{paper_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-a165e9628dfa04ad", "name": "Unused endpoint: POST /{paper_id}/reprocess", "shortDescription": {"text": "Unused endpoint: POST /{paper_id}/reprocess"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `POST /{paper_id}/reprocess` 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-0713825c6618ad4d", "name": "Unused endpoint: POST /{paper_id}/questions", "shortDescription": {"text": "Unused endpoint: POST /{paper_id}/questions"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `POST /{paper_id}/questions` 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-782936f24aa53fa5", "name": "Unused endpoint: DELETE /{paper_id}/questions/{qid}", "shortDescription": {"text": "Unused endpoint: DELETE /{paper_id}/questions/{qid}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `DELETE /{paper_id}/questions/{qid}` 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-feb8e5e2a34c5942", "name": "Unused endpoint: PATCH /{paper_id}/questions/{qid}", "shortDescription": {"text": "Unused endpoint: PATCH /{paper_id}/questions/{qid}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `PATCH /{paper_id}/questions/{qid}` 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-6bcd87078694f511", "name": "Unused endpoint: GET /{paper_id}/events", "shortDescription": {"text": "Unused endpoint: GET /{paper_id}/events"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `GET /{paper_id}/events` 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-34ddb8143209c933", "name": "Unused endpoint: GET /{paper_id}/page/{n}", "shortDescription": {"text": "Unused endpoint: GET /{paper_id}/page/{n}"}, "fullDescription": {"text": "`backend/app/routers/papers.py` declares `GET /{paper_id}/page/{n}` 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-304b6f2b403d93f7", "name": "Unused endpoint: POST /register", "shortDescription": {"text": "Unused endpoint: POST /register"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `POST /register` 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-618721b912bad1c2", "name": "Unused endpoint: POST /login", "shortDescription": {"text": "Unused endpoint: POST /login"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `POST /login` 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-99fc36db98c134ce", "name": "Unused endpoint: POST /logout", "shortDescription": {"text": "Unused endpoint: POST /logout"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `POST /logout` 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-fd1dc91abf32142d", "name": "Unused endpoint: GET /me", "shortDescription": {"text": "Unused endpoint: GET /me"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `GET /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-dbab2d3762ba5ff0", "name": "Unused endpoint: POST /password", "shortDescription": {"text": "Unused endpoint: POST /password"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `POST /password` 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-c9451b66f68ffc25", "name": "Unused endpoint: POST /invites", "shortDescription": {"text": "Unused endpoint: POST /invites"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `POST /invites` 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-5a436a81cf6d77eb", "name": "Unused endpoint: GET /invites", "shortDescription": {"text": "Unused endpoint: GET /invites"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `GET /invites` 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-07a1d2558a34c16f", "name": "Unused endpoint: DELETE /invites/{code}", "shortDescription": {"text": "Unused endpoint: DELETE /invites/{code}"}, "fullDescription": {"text": "`backend/app/routers/auth.py` declares `DELETE /invites/{code}` 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-e27251dbd9f297b5", "name": "Unused endpoint: POST /{qid}/override", "shortDescription": {"text": "Unused endpoint: POST /{qid}/override"}, "fullDescription": {"text": "`backend/app/routers/questions.py` declares `POST /{qid}/override` 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-252612fc9d1ef667", "name": "Unused endpoint: POST /{qid}/explain", "shortDescription": {"text": "Unused endpoint: POST /{qid}/explain"}, "fullDescription": {"text": "`backend/app/routers/questions.py` declares `POST /{qid}/explain` 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-e099c3775eaaa434", "name": "Unused endpoint: GET /{tag}/apk", "shortDescription": {"text": "Unused endpoint: GET /{tag}/apk"}, "fullDescription": {"text": "`backend/app/routers/releases.py` declares `GET /{tag}/apk` 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-9531c94e4fabe9b1", "name": "Unused endpoint: POST /submissions", "shortDescription": {"text": "Unused endpoint: POST /submissions"}, "fullDescription": {"text": "`backend/app/routers/jobs.py` declares `POST /submissions` 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-95dde20a492a9e82", "name": "Unused endpoint: POST /jobs/{job_id}/files", "shortDescription": {"text": "Unused endpoint: POST /jobs/{job_id}/files"}, "fullDescription": {"text": "`backend/app/routers/jobs.py` declares `POST /jobs/{job_id}/files` 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-1008c575819b3d37", "name": "Unused endpoint: GET /jobs", "shortDescription": {"text": "Unused endpoint: GET /jobs"}, "fullDescription": {"text": "`backend/app/routers/jobs.py` declares `GET /jobs` 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-81986784ebc79f89", "name": "Unused endpoint: GET /jobs/{job_id}/events", "shortDescription": {"text": "Unused endpoint: GET /jobs/{job_id}/events"}, "fullDescription": {"text": "`backend/app/routers/jobs.py` declares `GET /jobs/{job_id}/events` 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-82715e0a587f396d", "name": "Unused endpoint: GET /jobs/{job_id}/page/{n}", "shortDescription": {"text": "Unused endpoint: GET /jobs/{job_id}/page/{n}"}, "fullDescription": {"text": "`backend/app/routers/jobs.py` declares `GET /jobs/{job_id}/page/{n}` 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-e2ed0a3cfd4b8dd2", "name": "Unused endpoint: GET /mistakes", "shortDescription": {"text": "Unused endpoint: GET /mistakes"}, "fullDescription": {"text": "`backend/app/routers/insights.py` declares `GET /mistakes` 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-5032e3144ad00d34", "name": "Unused endpoint: GET /stats/overview", "shortDescription": {"text": "Unused endpoint: GET /stats/overview"}, "fullDescription": {"text": "`backend/app/routers/insights.py` declares `GET /stats/overview` 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-8e1fadad2500f063", "name": "Unused endpoint: POST /ask", "shortDescription": {"text": "Unused endpoint: POST /ask"}, "fullDescription": {"text": "`backend/app/routers/chat.py` declares `POST /ask` 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-0d3a434fedb1fa40", "name": "Unused endpoint: GET /chat", "shortDescription": {"text": "Unused endpoint: GET /chat"}, "fullDescription": {"text": "`backend/app/routers/chat.py` declares `GET /chat` 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-ada2584341c797f3", "name": "Unused endpoint: PATCH /{student_id}", "shortDescription": {"text": "Unused endpoint: PATCH /{student_id}"}, "fullDescription": {"text": "`backend/app/routers/students.py` declares `PATCH /{student_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-e131e06edbb79fdb", "name": "Unused endpoint: DELETE /{student_id}", "shortDescription": {"text": "Unused endpoint: DELETE /{student_id}"}, "fullDescription": {"text": "`backend/app/routers/students.py` declares `DELETE /{student_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}}]}}, "automationDetails": {"id": "repobility/22776"}, "properties": {"repository": "Winner-Nick/exam-tutor", "repoUrl": "https://github.com/Winner-Nick/exam-tutor", "branch": "main"}, "results": [{"ruleId": "scanner-03ce2999be0d5625", "level": "note", "message": {"text": "Possibly dead Python function: consolidate_user"}, "properties": {"repobilityId": "c0325bdcca97c4cb", "scanner": "scanner-primary", "fingerprint": "03ce2999be0d5625", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/prompts.py:151"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-972635630c4cf563", "level": "note", "message": {"text": "Possibly dead Python function: require_admin"}, "properties": {"repobilityId": "3c06db2e25566bf7", "scanner": "scanner-primary", "fingerprint": "972635630c4cf563", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/auth.py:81"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-8f8212c7a8211dc1", "level": "note", "message": {"text": "Possibly dead Python function: vision_extract_page"}, "properties": {"repobilityId": "472cf88d55fd5e95", "scanner": "scanner-primary", "fingerprint": "8f8212c7a8211dc1", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/llm.py:65"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-94ee9e645edf3aa0", "level": "note", "message": {"text": "Possibly dead Python function: call"}, "properties": {"repobilityId": "cf6e1a33b1245f9d", "scanner": "scanner-primary", "fingerprint": "94ee9e645edf3aa0", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/llm.py:164"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-bf96c4b03a100741", "level": "note", "message": {"text": "Possibly dead Python function: count_recent_uploads"}, "properties": {"repobilityId": "58771761dcda9639", "scanner": "scanner-primary", "fingerprint": "bf96c4b03a100741", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/store.py:306"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-5b54bb19b1e9f5b8", "level": "note", "message": {"text": "Possibly dead Python function: prog"}, "properties": {"repobilityId": "e0bee7ca19447ba1", "scanner": "scanner-primary", "fingerprint": "5b54bb19b1e9f5b8", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/pipeline.py:583"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-7b2613a7aaedcd12", "level": "note", "message": {"text": "Possibly dead Python function: save_upload_pdf"}, "properties": {"repobilityId": "1aed8f5925430f39", "scanner": "scanner-primary", "fingerprint": "7b2613a7aaedcd12", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/security.py:118"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-59f20d6f470c62bd", "level": "note", "message": {"text": "Possibly dead Python function: send_with_headers"}, "properties": {"repobilityId": "b1575092f407cff6", "scanner": "scanner-primary", "fingerprint": "59f20d6f470c62bd", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "backend/app/security.py:143"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-976ba2ae971393fb", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 frontend/src/components/Markdown.tsx:12"}, "properties": {"repobilityId": "94a982e0441f1633", "scanner": "scanner-primary", "fingerprint": "976ba2ae971393fb", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-43bced65a560106c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StatsPage.tsx:142"}, "properties": {"repobilityId": "999b67fc5946269e", "scanner": "scanner-primary", "fingerprint": "43bced65a560106c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c0acfdda8bcfdb96", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/SettingsPage.tsx:270"}, "properties": {"repobilityId": "44a7002df62c85a0", "scanner": "scanner-primary", "fingerprint": "c0acfdda8bcfdb96", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7ee2296ba4f4d2c6", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/DashboardPage.tsx:284"}, "properties": {"repobilityId": "98f645e60235535e", "scanner": "scanner-primary", "fingerprint": "7ee2296ba4f4d2c6", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-acafe3b1a5f97dde", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/StudentsPage.tsx:61"}, "properties": {"repobilityId": "73d7ead3ce2d419d", "scanner": "scanner-primary", "fingerprint": "acafe3b1a5f97dde", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-48772175319e1c2a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/JobPage.tsx:160"}, "properties": {"repobilityId": "a08bc6972ab50689", "scanner": "scanner-primary", "fingerprint": "48772175319e1c2a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6b58b9a7dc50056a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PapersPage.tsx:131"}, "properties": {"repobilityId": "9675f092fce250dc", "scanner": "scanner-primary", "fingerprint": "6b58b9a7dc50056a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d74bd91c10b7bf70", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 frontend/src/pages/PaperPage.tsx:218"}, "properties": {"repobilityId": "00bc4fdc0ed04d0e", "scanner": "scanner-primary", "fingerprint": "d74bd91c10b7bf70", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-14ee8745e4c03762", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in frontend/src/components/Markdown.tsx:12"}, "properties": {"repobilityId": "4178227b85281885", "scanner": "scanner-primary", "fingerprint": "14ee8745e4c03762", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "frontend/src/components/Markdown.tsx"}, "region": {"startLine": 12}}}]}, {"ruleId": "scanner-6429fdd60ee1ea1a", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "14a91952371e2965", "scanner": "scanner-primary", "fingerprint": "6429fdd60ee1ea1a", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/android-apk.yml"}, "region": {"startLine": 98}}}]}, {"ruleId": "scanner-9995793ac152fd7c", "level": "warning", "message": {"text": "GitHub Actions workflow grants broad write permissions"}, "properties": {"repobilityId": "40ecee7af19f7fef", "scanner": "scanner-primary", "fingerprint": "9995793ac152fd7c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "least-privilege"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/android-apk.yml"}, "region": {"startLine": 1}}}]}, {"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": "4e2f4a89694cbf72", "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": "e6becae0a2c1ec98", "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": "8940fabcee4eebef", "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": "2598a6c56b5c425e", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-11825279136b53a3", "level": "warning", "message": {"text": "CI is configured but no tests are detected"}, "properties": {"repobilityId": "7d22e338b4eba267", "scanner": "scanner-primary", "fingerprint": "11825279136b53a3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["tests", "ci", "config-theater", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-b9088664ace7f748", "level": "note", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "62cca4e80482aafc", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["production-readiness", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "50f7b55ccb26d10f", "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": "679f2e954decbe72", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-1b5a76780c3df9aa", "level": "none", "message": {"text": "1 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "d04379b337b44a15", "scanner": "scanner-primary", "fingerprint": "1b5a76780c3df9aa", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-be46ea126aa5d8dc", "level": "note", "message": {"text": "Near-duplicate function bodies in 3 places"}, "properties": {"repobilityId": "e2611ba715c77b5b", "scanner": "scanner-primary", "fingerprint": "be46ea126aa5d8dc", "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": "392e17de9faa0905", "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": "acc99877655f1215", "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": "9a38942cd78ef68b", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-be46ea126aa5d8dc", "level": "note", "message": {"text": "Near-duplicate function bodies in 3 places"}, "properties": {"repobilityId": "db8971b2673f61ad", "scanner": "scanner-primary", "fingerprint": "be46ea126aa5d8dc", "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": "7d59d728bffaec7a", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-0f7f405e7897f3ad", "level": "warning", "message": {"text": "Frontend route `/download` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "properties": {"repobilityId": "6d1af5c0648c1953", "scanner": "scanner-primary", "fingerprint": "0f7f405e7897f3ad", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-3017f1fe0af24f3a", "level": "warning", "message": {"text": "Frontend route `/jobs/:jobId` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "properties": {"repobilityId": "0a69a822a7839810", "scanner": "scanner-primary", "fingerprint": "3017f1fe0af24f3a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-9ffbd37caebcd744", "level": "warning", "message": {"text": "Frontend route `/papers/:paperId` has no Link/navigate to it \u2014 frontend/src/App.tsx"}, "properties": {"repobilityId": "2d01748f37a5d14f", "scanner": "scanner-primary", "fingerprint": "9ffbd37caebcd744", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-b52594fc05d6aea6", "level": "error", "message": {"text": "Dangling fetch: GET /api/auth/me (frontend/src/api.ts:77)"}, "properties": {"repobilityId": "72685113545cb66f", "scanner": "scanner-primary", "fingerprint": "b52594fc05d6aea6", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-5384d1b4ec898e2f", "level": "error", "message": {"text": "Dangling fetch: POST /api/auth/login (frontend/src/api.ts:79)"}, "properties": {"repobilityId": "6fabf04f031b53a8", "scanner": "scanner-primary", "fingerprint": "5384d1b4ec898e2f", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-91507804acf89d19", "level": "error", "message": {"text": "Dangling fetch: POST /api/auth/register (frontend/src/api.ts:81)"}, "properties": {"repobilityId": "f84ed1cfa49225e9", "scanner": "scanner-primary", "fingerprint": "91507804acf89d19", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-2a030bceeb99762b", "level": "error", "message": {"text": "Dangling fetch: POST /api/auth/logout (frontend/src/api.ts:85)"}, "properties": {"repobilityId": "2f4c3a5c8582b1bd", "scanner": "scanner-primary", "fingerprint": "2a030bceeb99762b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-de0dba035f953b47", "level": "error", "message": {"text": "Dangling fetch: POST /api/auth/password (frontend/src/api.ts:87)"}, "properties": {"repobilityId": "9b0fdfd8e2ae9f95", "scanner": "scanner-primary", "fingerprint": "de0dba035f953b47", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-c3f9d6c5d624b84a", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/invites (frontend/src/api.ts:92)"}, "properties": {"repobilityId": "ad4a222a175fca8e", "scanner": "scanner-primary", "fingerprint": "c3f9d6c5d624b84a", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-7af6d06d83466f22", "level": "error", "message": {"text": "Dangling fetch: GET /api/admin/invites (frontend/src/api.ts:96)"}, "properties": {"repobilityId": "31235b9cbae7a728", "scanner": "scanner-primary", "fingerprint": "7af6d06d83466f22", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-204c22e0d62391db", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/admin/invites/${encodeURIComponent(code)} (frontend/src/api.ts:98)"}, "properties": {"repobilityId": "148e024a64a62139", "scanner": "scanner-primary", "fingerprint": "204c22e0d62391db", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-3478a20d2d2c0543", "level": "error", "message": {"text": "Dangling fetch: GET /api/students (frontend/src/api.ts:103)"}, "properties": {"repobilityId": "2817475d4ff56c90", "scanner": "scanner-primary", "fingerprint": "3478a20d2d2c0543", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-4e8869e8f0c1ccad", "level": "error", "message": {"text": "Dangling fetch: POST /api/students (frontend/src/api.ts:105)"}, "properties": {"repobilityId": "0392c42e4c01043c", "scanner": "scanner-primary", "fingerprint": "4e8869e8f0c1ccad", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-7de7a3bce5a32b58", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/students/${id} (frontend/src/api.ts:107)"}, "properties": {"repobilityId": "3ff22c2ae3a46ce9", "scanner": "scanner-primary", "fingerprint": "7de7a3bce5a32b58", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-714527a3e97a51a1", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/students/${id} (frontend/src/api.ts:109)"}, "properties": {"repobilityId": "f7d0a67839e4acdd", "scanner": "scanner-primary", "fingerprint": "714527a3e97a51a1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-fa6f95d7daef08a4", "level": "error", "message": {"text": "Dangling fetch: GET /api/papers (frontend/src/api.ts:112)"}, "properties": {"repobilityId": "41258cd7b9029a2e", "scanner": "scanner-primary", "fingerprint": "fa6f95d7daef08a4", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-b8c454057d21fa28", "level": "error", "message": {"text": "Dangling fetch: GET /api/papers/${id} (frontend/src/api.ts:113)"}, "properties": {"repobilityId": "fe2781b5bfdb2104", "scanner": "scanner-primary", "fingerprint": "b8c454057d21fa28", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-a97b9d0b6f120403", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/papers/${id} (frontend/src/api.ts:132)"}, "properties": {"repobilityId": "75cf0c02040a47a8", "scanner": "scanner-primary", "fingerprint": "a97b9d0b6f120403", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-3bf8befca599a823", "level": "error", "message": {"text": "Dangling fetch: POST /api/papers/manual (frontend/src/api.ts:134)"}, "properties": {"repobilityId": "2d6e0504c76d8256", "scanner": "scanner-primary", "fingerprint": "3bf8befca599a823", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-64be6f3334933d87", "level": "error", "message": {"text": "Dangling fetch: POST /api/papers/${id}/questions (frontend/src/api.ts:139)"}, "properties": {"repobilityId": "99726418af1fb894", "scanner": "scanner-primary", "fingerprint": "64be6f3334933d87", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-93e9ee98f3583320", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/papers/${id}/questions/${qid} (frontend/src/api.ts:144)"}, "properties": {"repobilityId": "8ffd0bac1e21507c", "scanner": "scanner-primary", "fingerprint": "93e9ee98f3583320", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-88caeec8f85ab092", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/papers/${id} (frontend/src/api.ts:146)"}, "properties": {"repobilityId": "4691af74ae1476e2", "scanner": "scanner-primary", "fingerprint": "88caeec8f85ab092", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-88e374a8b5d8cec1", "level": "error", "message": {"text": "Dangling fetch: POST /api/papers/${id}/reprocess (frontend/src/api.ts:148)"}, "properties": {"repobilityId": "b3f78eb9c59ddccb", "scanner": "scanner-primary", "fingerprint": "88e374a8b5d8cec1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-c52c78e1b368488e", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/papers/${id}/questions/${qid} (frontend/src/api.ts:150)"}, "properties": {"repobilityId": "aff8e0e1d00ea742", "scanner": "scanner-primary", "fingerprint": "c52c78e1b368488e", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-1162d3e9be0b2b5d", "level": "error", "message": {"text": "Dangling fetch: POST /api/jobs/${jobId}/questions/${qid}/override (frontend/src/api.ts:187)"}, "properties": {"repobilityId": "d0287c2bba536dda", "scanner": "scanner-primary", "fingerprint": "1162d3e9be0b2b5d", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-deffa868025db22e", "level": "error", "message": {"text": "Dangling fetch: POST /api/jobs/${jobId}/ask (frontend/src/api.ts:199)"}, "properties": {"repobilityId": "315f3d729e48cd8c", "scanner": "scanner-primary", "fingerprint": "deffa868025db22e", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-3badeb1a0993aebc", "level": "error", "message": {"text": "Dangling fetch: GET /api/releases (frontend/src/api.ts:219)"}, "properties": {"repobilityId": "591929066351a93e", "scanner": "scanner-primary", "fingerprint": "3badeb1a0993aebc", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"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-91786ec9e40e27ca", "level": "note", "message": {"text": "Unused endpoint: GET /{path:path}"}, "properties": {"repobilityId": "01cc19314296c543", "scanner": "scanner-primary", "fingerprint": "91786ec9e40e27ca", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7a009b1a56794f45", "level": "note", "message": {"text": "Unused endpoint: POST /"}, "properties": {"repobilityId": "3ba1731e1036877b", "scanner": "scanner-primary", "fingerprint": "7a009b1a56794f45", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-af6608de4c7fbf85", "level": "note", "message": {"text": "Unused endpoint: POST /manual"}, "properties": {"repobilityId": "a8b8d5c5ad0fb8e7", "scanner": "scanner-primary", "fingerprint": "af6608de4c7fbf85", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7e441539d2fdcd6d", "level": "note", "message": {"text": "Unused endpoint: POST /{paper_id}/files"}, "properties": {"repobilityId": "95db437ad5d1ee3b", "scanner": "scanner-primary", "fingerprint": "7e441539d2fdcd6d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5baa8971ebe192a1", "level": "note", "message": {"text": "Unused endpoint: GET /"}, "properties": {"repobilityId": "1c31beadc1dc1a6c", "scanner": "scanner-primary", "fingerprint": "5baa8971ebe192a1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7a837f93f9c1d3ba", "level": "note", "message": {"text": "Unused endpoint: GET /{paper_id}"}, "properties": {"repobilityId": "46038c3f64e35cc2", "scanner": "scanner-primary", "fingerprint": "7a837f93f9c1d3ba", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-282fd09ae0dc7867", "level": "note", "message": {"text": "Unused endpoint: PATCH /{paper_id}"}, "properties": {"repobilityId": "39f9ece4e6894bac", "scanner": "scanner-primary", "fingerprint": "282fd09ae0dc7867", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-91941e41734a4fb4", "level": "note", "message": {"text": "Unused endpoint: DELETE /{paper_id}"}, "properties": {"repobilityId": "5a652f19db2017ec", "scanner": "scanner-primary", "fingerprint": "91941e41734a4fb4", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-a165e9628dfa04ad", "level": "note", "message": {"text": "Unused endpoint: POST /{paper_id}/reprocess"}, "properties": {"repobilityId": "81df7b233923dbb2", "scanner": "scanner-primary", "fingerprint": "a165e9628dfa04ad", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0713825c6618ad4d", "level": "note", "message": {"text": "Unused endpoint: POST /{paper_id}/questions"}, "properties": {"repobilityId": "add5a659e6869dea", "scanner": "scanner-primary", "fingerprint": "0713825c6618ad4d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-782936f24aa53fa5", "level": "note", "message": {"text": "Unused endpoint: DELETE /{paper_id}/questions/{qid}"}, "properties": {"repobilityId": "5fddd56c72aed047", "scanner": "scanner-primary", "fingerprint": "782936f24aa53fa5", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-feb8e5e2a34c5942", "level": "note", "message": {"text": "Unused endpoint: PATCH /{paper_id}/questions/{qid}"}, "properties": {"repobilityId": "bcce2dde55591e56", "scanner": "scanner-primary", "fingerprint": "feb8e5e2a34c5942", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-6bcd87078694f511", "level": "note", "message": {"text": "Unused endpoint: GET /{paper_id}/events"}, "properties": {"repobilityId": "2920ce90aa60d8dd", "scanner": "scanner-primary", "fingerprint": "6bcd87078694f511", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-34ddb8143209c933", "level": "note", "message": {"text": "Unused endpoint: GET /{paper_id}/page/{n}"}, "properties": {"repobilityId": "61286d0c3a240b11", "scanner": "scanner-primary", "fingerprint": "34ddb8143209c933", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-304b6f2b403d93f7", "level": "note", "message": {"text": "Unused endpoint: POST /register"}, "properties": {"repobilityId": "ff98778d2fd01543", "scanner": "scanner-primary", "fingerprint": "304b6f2b403d93f7", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-618721b912bad1c2", "level": "note", "message": {"text": "Unused endpoint: POST /login"}, "properties": {"repobilityId": "4f445a0b56cc60f5", "scanner": "scanner-primary", "fingerprint": "618721b912bad1c2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-99fc36db98c134ce", "level": "note", "message": {"text": "Unused endpoint: POST /logout"}, "properties": {"repobilityId": "e4f5be51032ff31a", "scanner": "scanner-primary", "fingerprint": "99fc36db98c134ce", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fd1dc91abf32142d", "level": "note", "message": {"text": "Unused endpoint: GET /me"}, "properties": {"repobilityId": "d59abcc5b893707a", "scanner": "scanner-primary", "fingerprint": "fd1dc91abf32142d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-dbab2d3762ba5ff0", "level": "note", "message": {"text": "Unused endpoint: POST /password"}, "properties": {"repobilityId": "bdc12199bb968c5e", "scanner": "scanner-primary", "fingerprint": "dbab2d3762ba5ff0", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-c9451b66f68ffc25", "level": "note", "message": {"text": "Unused endpoint: POST /invites"}, "properties": {"repobilityId": "b05665e329c7120d", "scanner": "scanner-primary", "fingerprint": "c9451b66f68ffc25", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5a436a81cf6d77eb", "level": "note", "message": {"text": "Unused endpoint: GET /invites"}, "properties": {"repobilityId": "24f397f72d5ee169", "scanner": "scanner-primary", "fingerprint": "5a436a81cf6d77eb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-07a1d2558a34c16f", "level": "note", "message": {"text": "Unused endpoint: DELETE /invites/{code}"}, "properties": {"repobilityId": "05a2bad771ff85a9", "scanner": "scanner-primary", "fingerprint": "07a1d2558a34c16f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e27251dbd9f297b5", "level": "note", "message": {"text": "Unused endpoint: POST /{qid}/override"}, "properties": {"repobilityId": "cb8dd2abd4e6cf34", "scanner": "scanner-primary", "fingerprint": "e27251dbd9f297b5", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-252612fc9d1ef667", "level": "note", "message": {"text": "Unused endpoint: POST /{qid}/explain"}, "properties": {"repobilityId": "469c137b4972a7d6", "scanner": "scanner-primary", "fingerprint": "252612fc9d1ef667", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e099c3775eaaa434", "level": "note", "message": {"text": "Unused endpoint: GET /{tag}/apk"}, "properties": {"repobilityId": "ecd804f569c739e0", "scanner": "scanner-primary", "fingerprint": "e099c3775eaaa434", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9531c94e4fabe9b1", "level": "note", "message": {"text": "Unused endpoint: POST /submissions"}, "properties": {"repobilityId": "f4b078e7f7972f5a", "scanner": "scanner-primary", "fingerprint": "9531c94e4fabe9b1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-95dde20a492a9e82", "level": "note", "message": {"text": "Unused endpoint: POST /jobs/{job_id}/files"}, "properties": {"repobilityId": "abfaa8743e1c471f", "scanner": "scanner-primary", "fingerprint": "95dde20a492a9e82", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1008c575819b3d37", "level": "note", "message": {"text": "Unused endpoint: GET /jobs"}, "properties": {"repobilityId": "067972aeb717cdc3", "scanner": "scanner-primary", "fingerprint": "1008c575819b3d37", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-81986784ebc79f89", "level": "note", "message": {"text": "Unused endpoint: GET /jobs/{job_id}/events"}, "properties": {"repobilityId": "cb69e4b6c4cb58de", "scanner": "scanner-primary", "fingerprint": "81986784ebc79f89", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-82715e0a587f396d", "level": "note", "message": {"text": "Unused endpoint: GET /jobs/{job_id}/page/{n}"}, "properties": {"repobilityId": "f80c1bd9ffbc30c6", "scanner": "scanner-primary", "fingerprint": "82715e0a587f396d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e2ed0a3cfd4b8dd2", "level": "note", "message": {"text": "Unused endpoint: GET /mistakes"}, "properties": {"repobilityId": "0f110a5a3151164a", "scanner": "scanner-primary", "fingerprint": "e2ed0a3cfd4b8dd2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5032e3144ad00d34", "level": "note", "message": {"text": "Unused endpoint: GET /stats/overview"}, "properties": {"repobilityId": "79a32114d1bad374", "scanner": "scanner-primary", "fingerprint": "5032e3144ad00d34", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-8e1fadad2500f063", "level": "note", "message": {"text": "Unused endpoint: POST /ask"}, "properties": {"repobilityId": "be0702c7a3a3373a", "scanner": "scanner-primary", "fingerprint": "8e1fadad2500f063", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0d3a434fedb1fa40", "level": "note", "message": {"text": "Unused endpoint: GET /chat"}, "properties": {"repobilityId": "fb97c825155965e7", "scanner": "scanner-primary", "fingerprint": "0d3a434fedb1fa40", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-ada2584341c797f3", "level": "note", "message": {"text": "Unused endpoint: PATCH /{student_id}"}, "properties": {"repobilityId": "c19b4ba8b26e4801", "scanner": "scanner-primary", "fingerprint": "ada2584341c797f3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e131e06edbb79fdb", "level": "note", "message": {"text": "Unused endpoint: DELETE /{student_id}"}, "properties": {"repobilityId": "9c0b2ef40e05b247", "scanner": "scanner-primary", "fingerprint": "e131e06edbb79fdb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}