{"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-da8f91d1c1a63648", "name": "Stray `console.log` in TS/JS \u2014 api/usage-notify.js:187", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 api/usage-notify.js:187"}, "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-141b6ed7172b523f", "name": "Stray `console.log` in TS/JS \u2014 api/guardian-register.js:167", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 api/guardian-register.js:167"}, "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-9912af7cdbcb2121", "name": "Stray `console.log` in TS/JS \u2014 api/waitlist.js:24", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 api/waitlist.js:24"}, "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-c5e00e6237fdd6f5", "name": "`truncate` class without `title=` for hover reveal \u2014 src/toddler/ToddlerApp.jsx:916", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/toddler/ToddlerApp.jsx:916"}, "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-d6461b3dbe44fcb7", "name": "Icon-only button without accessible name \u2014 src/components/ClassroomDashboard.jsx:137", "shortDescription": {"text": "Icon-only button without accessible name \u2014 src/components/ClassroomDashboard.jsx:137"}, "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-a49d8b3fda97a033", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/ClassroomDashboard.jsx:585", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ClassroomDashboard.jsx:585"}, "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-f0c5d268ba5aa42a", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/ParentZone.jsx:575", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ParentZone.jsx:575"}, "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-9568ca499ae13024", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/ProfileSelector.jsx:207", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ProfileSelector.jsx:207"}, "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-cd8cd55fc7c4d749", "name": "`truncate` class without `title=` for hover reveal \u2014 src/modules/WorldGK.jsx:693", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/modules/WorldGK.jsx:693"}, "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-48cf4437dfcce67d", "name": "`truncate` class without `title=` for hover reveal \u2014 src/pages/SchoolsPage.jsx:83", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/pages/SchoolsPage.jsx:83"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-8693d1bb31f34235", "name": "`truncate` class without `title=` for hover reveal \u2014 src/ks2/KS2App.jsx:371", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/ks2/KS2App.jsx:371"}, "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-166dff3c3df6a10e", "name": "ORM models found but no DB engine detected", "shortDescription": {"text": "ORM models found but no DB engine detected"}, "fullDescription": {"text": "The repo defines tables/models but no DB connection string was found. Likely lives in env vars or a config file the scanner didn't read."}, "properties": {"scanner": "scanner-primary", "layer": "data", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4a6aa2967566d1a1", "name": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-send.js:9", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-send.js:9"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e84cabfc0aa3e03c", "name": "Insecure pattern 'cors_wildcard' in functions/api/teacher-create-school.js:10", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-create-school.js:10"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f351e3c293fcaf81", "name": "Insecure pattern 'cors_wildcard' in functions/api/_class-session.js:58", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/_class-session.js:58"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-66e43a13fd3c74d0", "name": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-accept.js:9", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-accept.js:9"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-735afeec5a6494cb", "name": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-check.js:8", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-check.js:8"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-29ef8c61a3d23313", "name": "Insecure pattern 'cors_wildcard' in functions/api/tts.js:145", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in functions/api/tts.js:145"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-5f267a8fbdbad2b0", "name": "Possible secret in src/components/TeacherSetup.jsx", "shortDescription": {"text": "Possible secret in src/components/TeacherSetup.jsx"}, "fullDescription": {"text": "Detected pattern matching password_literal. Rotate the credential and move to a secret manager."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "critical", "confidence": 1.0}}, {"id": "scanner-f4ef7f1e541a94c9", "name": "Possible secret in src/components/GuardianSetup.jsx", "shortDescription": {"text": "Possible secret in src/components/GuardianSetup.jsx"}, "fullDescription": {"text": "Detected pattern matching password_literal. Rotate the credential and move to a secret manager."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "critical", "confidence": 1.0}}, {"id": "scanner-48e63c43966eddaa", "name": "Insecure pattern 'local_storage_auth_token' in src/hooks/useGuardian.js:60", "shortDescription": {"text": "Insecure pattern 'local_storage_auth_token' in src/hooks/useGuardian.js:60"}, "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-4601e3ad3bb28677", "name": "No CI/CD pipelines detected", "shortDescription": {"text": "No CI/CD pipelines detected"}, "fullDescription": {"text": "No GitHub Actions, GitLab CI, or CircleCI configs found. Without CI you can't gate deploys on tests/lints."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-600dfd20df4d28b4", "name": "Very large file: src/App.jsx (1549 lines)", "shortDescription": {"text": "Very large file: src/App.jsx (1549 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-59919d7a30f3a281", "name": "Very large file: src/toddler/ToddlerApp.jsx (1352 lines)", "shortDescription": {"text": "Very large file: src/toddler/ToddlerApp.jsx (1352 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-0a3612e9b7c40d03", "name": "Very large file: src/components/ClassroomDashboard.jsx (1073 lines)", "shortDescription": {"text": "Very large file: src/components/ClassroomDashboard.jsx (1073 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-6f2b9c7b7279e531", "name": "Very large file: src/components/ParentZone.jsx (1015 lines)", "shortDescription": {"text": "Very large file: src/components/ParentZone.jsx (1015 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-22d9b734a45d6a93", "name": "Very large file: src/components/Dashboard.jsx (1488 lines)", "shortDescription": {"text": "Very large file: src/components/Dashboard.jsx (1488 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-6fde9c26b0a3f52e", "name": "Very large file: src/modules/StoryRoom.jsx (994 lines)", "shortDescription": {"text": "Very large file: src/modules/StoryRoom.jsx (994 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-13e034b0d06143aa", "name": "Very large file: src/modules/SacredStories.jsx (1053 lines)", "shortDescription": {"text": "Very large file: src/modules/SacredStories.jsx (1053 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-af6ed338f85d07a9", "name": "Very large file: src/modules/SoundPop.jsx (1503 lines)", "shortDescription": {"text": "Very large file: src/modules/SoundPop.jsx (1503 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-e22937357011b8db", "name": "Very large file: src/modules/GameArcade.jsx (2452 lines)", "shortDescription": {"text": "Very large file: src/modules/GameArcade.jsx (2452 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-6a7aeb375400b85c", "name": "Very large file: src/pages/LandingPage.jsx (971 lines)", "shortDescription": {"text": "Very large file: src/pages/LandingPage.jsx (971 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": "4 test file(s) for 131 source file(s) (ratio 0.03). 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-faccb9061e9b52a0", "name": "No README detected", "shortDescription": {"text": "No README detected"}, "fullDescription": {"text": "No README file was found. Generated repos without README context are hard to operate, validate, or safely hand off."}, "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 8 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 45 placeholder/mock markers across 15 source files. This often means the repo looks complete while core flows still use generated scaffolding or fake data."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-9d79c4077342a7d0", "name": "Runtime service client appears to use placeholder configuration", "shortDescription": {"text": "Runtime service client appears to use placeholder configuration"}, "fullDescription": {"text": "A runtime source file appears to wire Supabase/Firebase/AI/payment-style clients to placeholder URLs, keys, or fallback values. In the Fable corpus this often means the UI/API shape is present while the backend service is not actually configured."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2d0c7b7ab8f8aacf", "name": "Critical user flow still appears backed by mock or placeholder data", "shortDescription": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "fullDescription": {"text": "A payment/auth/admin/order/billing-style flow contains mock, fake, TODO, dummy, or placeholder markers in runtime source. In the Fable corpus this is a high-leverage completeness smell: the app can look finished while the money, identity, or tenant flow is still scaffolded."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b9088664ace7f748", "name": "Composite production-readiness gap", "shortDescription": {"text": "Composite production-readiness gap"}, "fullDescription": {"text": "Multiple low-cost hardening controls are missing together: license, ci, operator-readme. Opus verification showed these co-occurring gaps are a better readiness signal than reading each flag in isolation."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-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-04068bf131c64b9c", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-checkout.js:56", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-checkout.js:56"}, "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-604315f20d358d10", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-send.js:55", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-send.js:55"}, "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-f597660be563cb5d", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-portal.js:42", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-portal.js:42"}, "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-63724d656d30aa36", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/weekly-digest.js:152", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/weekly-digest.js:152"}, "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-d759a214d19f77c4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/usage-notify.js:135", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/usage-notify.js:135"}, "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-ca3c456e0afa4e69", "name": "Commented-code block (5 lines) in functions/api/teacher-create-school.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in functions/api/teacher-create-school.js:1"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ea37e531bd208228", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-create-school.js:51", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-create-school.js: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-3d49cc987f767305", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/error-report.js:34", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/error-report.js:34"}, "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-3cd9fa1a018f0589", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-save.js:27", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-save.js:27"}, "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-0a60e2f3fa7150f1", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-accept.js:38", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-accept.js: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-6fb63b3fb3f61b5e", "name": "Commented-code block (5 lines) in functions/api/stripe-webhook.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in functions/api/stripe-webhook.js:1"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-dcd9647a8c0d19e1", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-webhook.js:43", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-webhook.js: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-ed3427dbb22c6636", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/guardian-register.js:116", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/guardian-register.js:116"}, "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-323abba1cde81135", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-load.js:17", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-load.js: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-19e2b840bfb228b8", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-check.js:23", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-check.js:23"}, "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-f6c0608a3ac21eea", "name": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-roster.js:17", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-roster.js: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-909b004cf2c0e6a1", "name": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-invite-send.js:91", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-invite-send.js: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-1f1cdc489564cfa4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 api/weekly-digest.js:117", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/weekly-digest.js:117"}, "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-3212ac62afeb1d12", "name": "`fetch()` without try/.catch or AbortSignal \u2014 api/usage-notify.js:199", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/usage-notify.js:199"}, "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-a6a020bac588c4fc", "name": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-create-school.js:56", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-create-school.js:56"}, "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-92cf94884410eb73", "name": "`fetch()` without try/.catch or AbortSignal \u2014 api/guardian-register.js:179", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/guardian-register.js:179"}, "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-e514f1b0b112965d", "name": "Legacy-named symbol `eduapp_guardian_v1` in src/App.jsx:1053", "shortDescription": {"text": "Legacy-named symbol `eduapp_guardian_v1` in src/App.jsx:1053"}, "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-948caa94e004d88d", "name": "Commented-code block (6 lines) in src/config/premiumContent.js:5", "shortDescription": {"text": "Commented-code block (6 lines) in src/config/premiumContent.js:5"}, "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-2eec71374af4831e", "name": "Legacy-named symbol `eduapp_guardian_v1` in src/utils/guardian.js:1", "shortDescription": {"text": "Legacy-named symbol `eduapp_guardian_v1` in src/utils/guardian.js:1"}, "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-0d9c746cf7839474", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/utils/weeklyDigest.js:81", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/utils/weeklyDigest.js:81"}, "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-9fb23595760b3ab1", "name": "Legacy-named symbol `eduapp_installation_id_v1` in src/utils/analytics.js:3", "shortDescription": {"text": "Legacy-named symbol `eduapp_installation_id_v1` in src/utils/analytics.js:3"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-10ba14a795d5b1c3", "name": "Commented-code block (6 lines) in src/utils/analytics.js:98", "shortDescription": {"text": "Commented-code block (6 lines) in src/utils/analytics.js:98"}, "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-e14aba08d4d1c2f7", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/TeacherSetup.jsx:73", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/TeacherSetup.jsx:73"}, "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-adab9980c9a7cf11", "name": "Legacy-named symbol `eduapp_install_nudge_dismissed_v1` in src/components/InstallNudge.jsx:5", "shortDescription": {"text": "Legacy-named symbol `eduapp_install_nudge_dismissed_v1` in src/components/InstallNudge.jsx:5"}, "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-c68af919856207d7", "name": "Commented-code block (5 lines) in src/modules/FunExercise.jsx:28", "shortDescription": {"text": "Commented-code block (5 lines) in src/modules/FunExercise.jsx:28"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-dc51504f71a8518e", "name": "Legacy-named symbol `eduapp_profiles_v1` in src/hooks/useProfiles.js:6", "shortDescription": {"text": "Legacy-named symbol `eduapp_profiles_v1` in src/hooks/useProfiles.js:6"}, "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-36ad95e9852f1f4f", "name": "Legacy-named symbol `eduapp_session_v1` in src/hooks/useGuardian.js:60", "shortDescription": {"text": "Legacy-named symbol `eduapp_session_v1` in src/hooks/useGuardian.js:60"}, "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-e2cdf368e675861c", "name": "Commented-code block (5 lines) in src/hooks/useGuardian.js:128", "shortDescription": {"text": "Commented-code block (5 lines) in src/hooks/useGuardian.js:128"}, "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-fd00d2842cd6489b", "name": "Legacy-named symbol `yaagvi_progress_v1` in src/hooks/useProgress.js:9", "shortDescription": {"text": "Legacy-named symbol `yaagvi_progress_v1` in src/hooks/useProgress.js:9"}, "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-8d3c8b55693e016b", "name": "Legacy-named symbol `eduapp_class_session_v1` in src/services/cloudStore.js:44", "shortDescription": {"text": "Legacy-named symbol `eduapp_class_session_v1` in src/services/cloudStore.js:44"}, "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-532eeda6e9cb4b54", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/services/cloudStore.js:403", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/services/cloudStore.js:403"}, "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-2eafc3188c36bb02", "name": "3 env vars used in code but missing from .env.example", "shortDescription": {"text": "3 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `DEV`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_URL`. 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}}]}}, "automationDetails": {"id": "repobility/18907"}, "properties": {"repository": "sanjuveed-debug/bloom-juniors", "repoUrl": "https://github.com/sanjuveed-debug/bloom-juniors", "branch": "main"}, "results": [{"ruleId": "scanner-da8f91d1c1a63648", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 api/usage-notify.js:187"}, "properties": {"repobilityId": "a70e47523717a664", "scanner": "scanner-primary", "fingerprint": "da8f91d1c1a63648", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-141b6ed7172b523f", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 api/guardian-register.js:167"}, "properties": {"repobilityId": "f3da9a4abf931ded", "scanner": "scanner-primary", "fingerprint": "141b6ed7172b523f", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-9912af7cdbcb2121", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 api/waitlist.js:24"}, "properties": {"repobilityId": "c2fbb1ddbd00914d", "scanner": "scanner-primary", "fingerprint": "9912af7cdbcb2121", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-c5e00e6237fdd6f5", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/toddler/ToddlerApp.jsx:916"}, "properties": {"repobilityId": "a07f400355b39999", "scanner": "scanner-primary", "fingerprint": "c5e00e6237fdd6f5", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d6461b3dbe44fcb7", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 src/components/ClassroomDashboard.jsx:137"}, "properties": {"repobilityId": "1011a4ca1f4ddfc8", "scanner": "scanner-primary", "fingerprint": "d6461b3dbe44fcb7", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-a49d8b3fda97a033", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ClassroomDashboard.jsx:585"}, "properties": {"repobilityId": "de2537642afe7c3e", "scanner": "scanner-primary", "fingerprint": "a49d8b3fda97a033", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-f0c5d268ba5aa42a", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ParentZone.jsx:575"}, "properties": {"repobilityId": "0553e1eae9e15242", "scanner": "scanner-primary", "fingerprint": "f0c5d268ba5aa42a", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9568ca499ae13024", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/ProfileSelector.jsx:207"}, "properties": {"repobilityId": "7f5f4ae7910637fc", "scanner": "scanner-primary", "fingerprint": "9568ca499ae13024", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-cd8cd55fc7c4d749", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/modules/WorldGK.jsx:693"}, "properties": {"repobilityId": "f50d0320d7210e49", "scanner": "scanner-primary", "fingerprint": "cd8cd55fc7c4d749", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-48cf4437dfcce67d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/pages/SchoolsPage.jsx:83"}, "properties": {"repobilityId": "d87973a6e9adcf74", "scanner": "scanner-primary", "fingerprint": "48cf4437dfcce67d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-8693d1bb31f34235", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/ks2/KS2App.jsx:371"}, "properties": {"repobilityId": "f9b714744a1240cb", "scanner": "scanner-primary", "fingerprint": "8693d1bb31f34235", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-166dff3c3df6a10e", "level": "warning", "message": {"text": "ORM models found but no DB engine detected"}, "properties": {"repobilityId": "cda36de65e114635", "scanner": "scanner-primary", "fingerprint": "166dff3c3df6a10e", "layer": "data", "severity": "medium", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-4a6aa2967566d1a1", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-send.js:9"}, "properties": {"repobilityId": "bbb385bd179c26f3", "scanner": "scanner-primary", "fingerprint": "4a6aa2967566d1a1", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/teacher-invite-send.js"}, "region": {"startLine": 9}}}]}, {"ruleId": "scanner-e84cabfc0aa3e03c", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-create-school.js:10"}, "properties": {"repobilityId": "d486efc4cf441dbc", "scanner": "scanner-primary", "fingerprint": "e84cabfc0aa3e03c", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/teacher-create-school.js"}, "region": {"startLine": 10}}}]}, {"ruleId": "scanner-f351e3c293fcaf81", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/_class-session.js:58"}, "properties": {"repobilityId": "548cbfc301e4e428", "scanner": "scanner-primary", "fingerprint": "f351e3c293fcaf81", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/_class-session.js"}, "region": {"startLine": 58}}}]}, {"ruleId": "scanner-66e43a13fd3c74d0", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-accept.js:9"}, "properties": {"repobilityId": "d925cc5e9069c241", "scanner": "scanner-primary", "fingerprint": "66e43a13fd3c74d0", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/teacher-invite-accept.js"}, "region": {"startLine": 9}}}]}, {"ruleId": "scanner-735afeec5a6494cb", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/teacher-invite-check.js:8"}, "properties": {"repobilityId": "dca1392b7e66a8ad", "scanner": "scanner-primary", "fingerprint": "735afeec5a6494cb", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/teacher-invite-check.js"}, "region": {"startLine": 8}}}]}, {"ruleId": "scanner-29ef8c61a3d23313", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in functions/api/tts.js:145"}, "properties": {"repobilityId": "70f19bf8ef33c614", "scanner": "scanner-primary", "fingerprint": "29ef8c61a3d23313", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "functions/api/tts.js"}, "region": {"startLine": 145}}}]}, {"ruleId": "scanner-5f267a8fbdbad2b0", "level": "error", "message": {"text": "Possible secret in src/components/TeacherSetup.jsx"}, "properties": {"repobilityId": "f4dacb8987afe4ba", "scanner": "scanner-primary", "fingerprint": "5f267a8fbdbad2b0", "layer": "security", "severity": "critical", "confidence": 1.0, "tags": ["secrets"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/components/TeacherSetup.jsx"}, "region": {"startLine": 100}}}]}, {"ruleId": "scanner-5f267a8fbdbad2b0", "level": "error", "message": {"text": "Possible secret in src/components/TeacherSetup.jsx"}, "properties": {"repobilityId": "f4dacb8987afe4ba", "scanner": "scanner-primary", "fingerprint": "5f267a8fbdbad2b0", "layer": "security", "severity": "critical", "confidence": 1.0, "tags": ["secrets"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/components/TeacherSetup.jsx"}, "region": {"startLine": 101}}}]}, {"ruleId": "scanner-f4ef7f1e541a94c9", "level": "error", "message": {"text": "Possible secret in src/components/GuardianSetup.jsx"}, "properties": {"repobilityId": "8bb6eee8751b86ae", "scanner": "scanner-primary", "fingerprint": "f4ef7f1e541a94c9", "layer": "security", "severity": "critical", "confidence": 1.0, "tags": ["secrets"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/components/GuardianSetup.jsx"}, "region": {"startLine": 57}}}]}, {"ruleId": "scanner-f4ef7f1e541a94c9", "level": "error", "message": {"text": "Possible secret in src/components/GuardianSetup.jsx"}, "properties": {"repobilityId": "8bb6eee8751b86ae", "scanner": "scanner-primary", "fingerprint": "f4ef7f1e541a94c9", "layer": "security", "severity": "critical", "confidence": 1.0, "tags": ["secrets"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/components/GuardianSetup.jsx"}, "region": {"startLine": 58}}}]}, {"ruleId": "scanner-48e63c43966eddaa", "level": "warning", "message": {"text": "Insecure pattern 'local_storage_auth_token' in src/hooks/useGuardian.js:60"}, "properties": {"repobilityId": "c25b848142869621", "scanner": "scanner-primary", "fingerprint": "48e63c43966eddaa", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "local_storage_auth_token"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/hooks/useGuardian.js"}, "region": {"startLine": 60}}}]}, {"ruleId": "scanner-4601e3ad3bb28677", "level": "warning", "message": {"text": "No CI/CD pipelines detected"}, "properties": {"repobilityId": "c3ee439bce2bc51e", "scanner": "scanner-primary", "fingerprint": "4601e3ad3bb28677", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-600dfd20df4d28b4", "level": "note", "message": {"text": "Very large file: src/App.jsx (1549 lines)"}, "properties": {"repobilityId": "a59a84e7052cf35b", "scanner": "scanner-primary", "fingerprint": "600dfd20df4d28b4", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-59919d7a30f3a281", "level": "note", "message": {"text": "Very large file: src/toddler/ToddlerApp.jsx (1352 lines)"}, "properties": {"repobilityId": "5609ca036179252d", "scanner": "scanner-primary", "fingerprint": "59919d7a30f3a281", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-0a3612e9b7c40d03", "level": "note", "message": {"text": "Very large file: src/components/ClassroomDashboard.jsx (1073 lines)"}, "properties": {"repobilityId": "1b4b60efe9dc6673", "scanner": "scanner-primary", "fingerprint": "0a3612e9b7c40d03", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-6f2b9c7b7279e531", "level": "note", "message": {"text": "Very large file: src/components/ParentZone.jsx (1015 lines)"}, "properties": {"repobilityId": "59cc09a9444cb97a", "scanner": "scanner-primary", "fingerprint": "6f2b9c7b7279e531", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-22d9b734a45d6a93", "level": "note", "message": {"text": "Very large file: src/components/Dashboard.jsx (1488 lines)"}, "properties": {"repobilityId": "cab36fe8eb035890", "scanner": "scanner-primary", "fingerprint": "22d9b734a45d6a93", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-6fde9c26b0a3f52e", "level": "note", "message": {"text": "Very large file: src/modules/StoryRoom.jsx (994 lines)"}, "properties": {"repobilityId": "ab609343b4ee0798", "scanner": "scanner-primary", "fingerprint": "6fde9c26b0a3f52e", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-13e034b0d06143aa", "level": "note", "message": {"text": "Very large file: src/modules/SacredStories.jsx (1053 lines)"}, "properties": {"repobilityId": "443b4eac5be79310", "scanner": "scanner-primary", "fingerprint": "13e034b0d06143aa", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-af6ed338f85d07a9", "level": "note", "message": {"text": "Very large file: src/modules/SoundPop.jsx (1503 lines)"}, "properties": {"repobilityId": "9c3df1e5e7fe6385", "scanner": "scanner-primary", "fingerprint": "af6ed338f85d07a9", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-e22937357011b8db", "level": "note", "message": {"text": "Very large file: src/modules/GameArcade.jsx (2452 lines)"}, "properties": {"repobilityId": "482148a076ce4bb7", "scanner": "scanner-primary", "fingerprint": "e22937357011b8db", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-6a7aeb375400b85c", "level": "note", "message": {"text": "Very large file: src/pages/LandingPage.jsx (971 lines)"}, "properties": {"repobilityId": "e057c3a6bad8ddbe", "scanner": "scanner-primary", "fingerprint": "6a7aeb375400b85c", "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": "57587b9ecf95f4b5", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-faccb9061e9b52a0", "level": "note", "message": {"text": "No README detected"}, "properties": {"repobilityId": "568f29112652104e", "scanner": "scanner-primary", "fingerprint": "faccb9061e9b52a0", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["docs", "readme", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "bf7d7f075dbd1bc9", "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": "1fad107bfeee5b2e", "scanner": "scanner-primary", "fingerprint": "72b2a6250083a784", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "incomplete", "generated-repo-pattern"]}}, {"ruleId": "scanner-9d79c4077342a7d0", "level": "warning", "message": {"text": "Runtime service client appears to use placeholder configuration"}, "properties": {"repobilityId": "f138228eb557b017", "scanner": "scanner-primary", "fingerprint": "9d79c4077342a7d0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "runtime-config", "service-client", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "691b2dfdfdd9f597", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-b9088664ace7f748", "level": "warning", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "a47e1e764f0b9091", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "medium", "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": "d82511bd8ac47f67", "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": "c8fd50b93aadba48", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-04068bf131c64b9c", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-checkout.js:56"}, "properties": {"repobilityId": "e56c14b32370535d", "scanner": "scanner-primary", "fingerprint": "04068bf131c64b9c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-604315f20d358d10", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-send.js:55"}, "properties": {"repobilityId": "44e06a1829a4624f", "scanner": "scanner-primary", "fingerprint": "604315f20d358d10", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f597660be563cb5d", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-portal.js:42"}, "properties": {"repobilityId": "276b5e2032a65291", "scanner": "scanner-primary", "fingerprint": "f597660be563cb5d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-63724d656d30aa36", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/weekly-digest.js:152"}, "properties": {"repobilityId": "b8a660106e1d86c4", "scanner": "scanner-primary", "fingerprint": "63724d656d30aa36", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d759a214d19f77c4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/usage-notify.js:135"}, "properties": {"repobilityId": "126be5999f8fd067", "scanner": "scanner-primary", "fingerprint": "d759a214d19f77c4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ca3c456e0afa4e69", "level": "none", "message": {"text": "Commented-code block (5 lines) in functions/api/teacher-create-school.js:1"}, "properties": {"repobilityId": "19f160f01851d730", "scanner": "scanner-primary", "fingerprint": "ca3c456e0afa4e69", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-ea37e531bd208228", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-create-school.js:51"}, "properties": {"repobilityId": "8d87c4cfd195ac11", "scanner": "scanner-primary", "fingerprint": "ea37e531bd208228", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3d49cc987f767305", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/error-report.js:34"}, "properties": {"repobilityId": "606791312026cfdb", "scanner": "scanner-primary", "fingerprint": "3d49cc987f767305", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3cd9fa1a018f0589", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-save.js:27"}, "properties": {"repobilityId": "e9319bad2ea28fc8", "scanner": "scanner-primary", "fingerprint": "3cd9fa1a018f0589", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-0a60e2f3fa7150f1", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-accept.js:38"}, "properties": {"repobilityId": "0b836f8a8a98d52a", "scanner": "scanner-primary", "fingerprint": "0a60e2f3fa7150f1", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-6fb63b3fb3f61b5e", "level": "none", "message": {"text": "Commented-code block (5 lines) in functions/api/stripe-webhook.js:1"}, "properties": {"repobilityId": "ffe1eafaa2f2413c", "scanner": "scanner-primary", "fingerprint": "6fb63b3fb3f61b5e", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-dcd9647a8c0d19e1", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/stripe-webhook.js:43"}, "properties": {"repobilityId": "022fe541f071fc4c", "scanner": "scanner-primary", "fingerprint": "dcd9647a8c0d19e1", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ed3427dbb22c6636", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/guardian-register.js:116"}, "properties": {"repobilityId": "61acb0ef1476c0f4", "scanner": "scanner-primary", "fingerprint": "ed3427dbb22c6636", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-323abba1cde81135", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-progress-load.js:17"}, "properties": {"repobilityId": "83e8eca40b6dd79c", "scanner": "scanner-primary", "fingerprint": "323abba1cde81135", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-19e2b840bfb228b8", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/teacher-invite-check.js:23"}, "properties": {"repobilityId": "3f95470bf69a584c", "scanner": "scanner-primary", "fingerprint": "19e2b840bfb228b8", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f6c0608a3ac21eea", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 functions/api/class-roster.js:17"}, "properties": {"repobilityId": "71ec913cf3aafd73", "scanner": "scanner-primary", "fingerprint": "f6c0608a3ac21eea", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-909b004cf2c0e6a1", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-invite-send.js:91"}, "properties": {"repobilityId": "3ed6512a2f0cabf4", "scanner": "scanner-primary", "fingerprint": "909b004cf2c0e6a1", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-1f1cdc489564cfa4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/weekly-digest.js:117"}, "properties": {"repobilityId": "b68bbdf43987a366", "scanner": "scanner-primary", "fingerprint": "1f1cdc489564cfa4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3212ac62afeb1d12", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/usage-notify.js:199"}, "properties": {"repobilityId": "9fb0fad04fb72d94", "scanner": "scanner-primary", "fingerprint": "3212ac62afeb1d12", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a6a020bac588c4fc", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/teacher-create-school.js:56"}, "properties": {"repobilityId": "1c5cf407e1b1c4c9", "scanner": "scanner-primary", "fingerprint": "a6a020bac588c4fc", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-92cf94884410eb73", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 api/guardian-register.js:179"}, "properties": {"repobilityId": "7d20499884eb0ca1", "scanner": "scanner-primary", "fingerprint": "92cf94884410eb73", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e514f1b0b112965d", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_guardian_v1` in src/App.jsx:1053"}, "properties": {"repobilityId": "41b0fb50e1637fb8", "scanner": "scanner-primary", "fingerprint": "e514f1b0b112965d", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-948caa94e004d88d", "level": "none", "message": {"text": "Commented-code block (6 lines) in src/config/premiumContent.js:5"}, "properties": {"repobilityId": "56fe35a1847d54cb", "scanner": "scanner-primary", "fingerprint": "948caa94e004d88d", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-2eec71374af4831e", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_guardian_v1` in src/utils/guardian.js:1"}, "properties": {"repobilityId": "6d865315fa62ad3a", "scanner": "scanner-primary", "fingerprint": "2eec71374af4831e", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-0d9c746cf7839474", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/utils/weeklyDigest.js:81"}, "properties": {"repobilityId": "756cb8804d0e1635", "scanner": "scanner-primary", "fingerprint": "0d9c746cf7839474", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-9fb23595760b3ab1", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_installation_id_v1` in src/utils/analytics.js:3"}, "properties": {"repobilityId": "6e049c73824a0e67", "scanner": "scanner-primary", "fingerprint": "9fb23595760b3ab1", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-10ba14a795d5b1c3", "level": "none", "message": {"text": "Commented-code block (6 lines) in src/utils/analytics.js:98"}, "properties": {"repobilityId": "4163c8c569dd8aaf", "scanner": "scanner-primary", "fingerprint": "10ba14a795d5b1c3", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-e14aba08d4d1c2f7", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/TeacherSetup.jsx:73"}, "properties": {"repobilityId": "8e4179af797f3870", "scanner": "scanner-primary", "fingerprint": "e14aba08d4d1c2f7", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-adab9980c9a7cf11", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_install_nudge_dismissed_v1` in src/components/InstallNudge.jsx:5"}, "properties": {"repobilityId": "fdca20aa28a84238", "scanner": "scanner-primary", "fingerprint": "adab9980c9a7cf11", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-c68af919856207d7", "level": "none", "message": {"text": "Commented-code block (5 lines) in src/modules/FunExercise.jsx:28"}, "properties": {"repobilityId": "6ddaf12972c6c405", "scanner": "scanner-primary", "fingerprint": "c68af919856207d7", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-dc51504f71a8518e", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_profiles_v1` in src/hooks/useProfiles.js:6"}, "properties": {"repobilityId": "6b545cf7aabcb68b", "scanner": "scanner-primary", "fingerprint": "dc51504f71a8518e", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-36ad95e9852f1f4f", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_session_v1` in src/hooks/useGuardian.js:60"}, "properties": {"repobilityId": "eeb82665e0136056", "scanner": "scanner-primary", "fingerprint": "36ad95e9852f1f4f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-e2cdf368e675861c", "level": "none", "message": {"text": "Commented-code block (5 lines) in src/hooks/useGuardian.js:128"}, "properties": {"repobilityId": "ceadf5ba9759d8a3", "scanner": "scanner-primary", "fingerprint": "e2cdf368e675861c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-fd00d2842cd6489b", "level": "note", "message": {"text": "Legacy-named symbol `yaagvi_progress_v1` in src/hooks/useProgress.js:9"}, "properties": {"repobilityId": "8ad4cb6e67a469df", "scanner": "scanner-primary", "fingerprint": "fd00d2842cd6489b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-8d3c8b55693e016b", "level": "note", "message": {"text": "Legacy-named symbol `eduapp_class_session_v1` in src/services/cloudStore.js:44"}, "properties": {"repobilityId": "4b07da4dcfd5c2ed", "scanner": "scanner-primary", "fingerprint": "8d3c8b55693e016b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-532eeda6e9cb4b54", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/services/cloudStore.js:403"}, "properties": {"repobilityId": "7013a4375411c8b2", "scanner": "scanner-primary", "fingerprint": "532eeda6e9cb4b54", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-2eafc3188c36bb02", "level": "none", "message": {"text": "3 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "bd934ba112b02ffa", "scanner": "scanner-primary", "fingerprint": "2eafc3188c36bb02", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}]}]}