{"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-7f9ae35a79f43111", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/test-supabase.js:19", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/test-supabase.js:19"}, "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-ef7e5f4fa16a4df8", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/test-prisma.js:12", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/test-prisma.js:12"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-68d2980922b80265", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/seed-mock-data.js:26", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/seed-mock-data.js:26"}, "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-27a1dd67834d627d", "name": "Stray `console.log` in TS/JS \u2014 server/scripts/upload-thumbnails.js:37", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/upload-thumbnails.js:37"}, "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-34667642a0d2b73d", "name": "Stray `console.log` in TS/JS \u2014 server/src/index.js:16", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/index.js:16"}, "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-cebba6cb2f70615d", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/enrichment.service.js:91", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/enrichment.service.js:91"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1d87aa7699b5d65d", "name": "Stray `console.log` in TS/JS \u2014 server/src/services/ingestion.service.js:53", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/ingestion.service.js:53"}, "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-761131c4db408135", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/CategoryPage.jsx:58", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/CategoryPage.jsx:58"}, "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-741e6656f9784553", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectDetail.jsx:234", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectDetail.jsx:234"}, "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-34bb307287c6ec58", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/LibraryPage.jsx:240", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/LibraryPage.jsx:240"}, "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-c17181ba63a2a286", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectsPage.jsx:44", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectsPage.jsx:44"}, "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-c957b844152bbe18", "name": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:165", "shortDescription": {"text": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:165"}, "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-77d975575340fb87", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:134", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:134"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-7efa36b76413bc1c", "name": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/CurationPage.jsx:110", "shortDescription": {"text": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/CurationPage.jsx:110"}, "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-ba72f5c6df47798f", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/CurationPage.jsx:194", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/CurationPage.jsx:194"}, "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-304e7cceb9e73aac", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/ReferencesAdminPage.jsx:174", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/ReferencesAdminPage.jsx:174"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3165f13821cc9725", "name": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/DigestAdminPage.jsx:194", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/DigestAdminPage.jsx:194"}, "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-1fc5d583a259d3ea", "name": "Insecure pattern 'direct_innerhtml_assignment' in docs/00-cadrage/business/decks/deck-180degres.html:557", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/00-cadrage/business/decks/deck-180degres.html:557"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8e47d46387a7fd02", "name": "Very large file: veille-creative/src/pages/admin/CurationPage.jsx (1719 lines)", "shortDescription": {"text": "Very large file: veille-creative/src/pages/admin/CurationPage.jsx (1719 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ea3b5e389d8c9c0f", "name": "Low test-to-source ratio", "shortDescription": {"text": "Low test-to-source ratio"}, "fullDescription": {"text": "8 tests / 67 src (ratio 0.12)."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-141b30a41e03817b", "name": "No license file detected", "shortDescription": {"text": "No license file detected"}, "fullDescription": {"text": "No LICENSE/COPYING/NOTICE file was found. Generated repositories often omit licensing, which blocks reuse and automated intake."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 62 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 71 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-2d0c7b7ab8f8aacf", "name": "Critical user flow still appears backed by mock or placeholder data", "shortDescription": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "fullDescription": {"text": "A payment/auth/admin/order/billing-style flow contains mock, fake, TODO, dummy, or placeholder markers in runtime source. In the Fable corpus this is a high-leverage completeness smell: the app can look finished while the money, identity, or tenant flow is still scaffolded."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-749d4bc1bd66df5f", "name": "Agent instructions exist but release-hardening basics are missing", "shortDescription": {"text": "Agent instructions exist but release-hardening basics are missing"}, "fullDescription": {"text": "AI-coder instruction files were found, but the repo is missing license. Treat this as a contract gap: the agent is guided, but the generated output is not yet guarded by the controls that make it repeatable."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f9f933452fcf93b9", "name": "Agent authority lacks a verifier contract: .claude/commands/pm-probe.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: .claude/commands/pm-probe.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-5b07939703846476", "name": "Agent authority lacks a verifier contract: .claude/commands/pm-prioritize.md", "shortDescription": {"text": "Agent authority lacks a verifier contract: .claude/commands/pm-prioritize.md"}, "fullDescription": {"text": "This agent instruction grants code or shell authority but does not state the verification gate that decides promotion. The recurring safe pattern is: LLM proposes; deterministic tests/build/security checks verify; only verified code promotes."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d1d2387f9eb33445", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/lib/youtube.client.js:34", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/lib/youtube.client.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-46e46c635561a742", "name": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/services/thumbnail.service.js:47", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/services/thumbnail.service.js:47"}, "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-0fe6c640c6590f4b", "name": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/pages/CategoryPage.jsx:25", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/pages/CategoryPage.jsx:25"}, "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-fe6c126df7983385", "name": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/admin-api.js:19", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/admin-api.js:19"}, "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-354b3414cf4d0305", "name": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/api.js:7", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/api.js:7"}, "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: `EMAIL_FROM`, `RESEND_API_KEY`, `VITE_API_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}}, {"id": "scanner-0509b90847f822b8", "name": "Frontend route `/t/:token` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/t/:token` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a829fddabd5e1919", "name": "Frontend route `/library/section/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/library/section/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-dd47a6e26b6a018c", "name": "Frontend route `/projects/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/projects/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d582a017fcdfd2dd", "name": "Frontend route `/digest/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/digest/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-8070c8b7e9521e8a", "name": "Frontend route `/surprises` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/surprises` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-eca39b8aa2e5367c", "name": "Frontend route `/admin` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `/admin` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a6e068d89a0fcec3", "name": "Frontend route `curation` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `curation` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-090a9d87f3866733", "name": "Frontend route `references` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `references` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-35c671736dc862f0", "name": "Frontend route `sections` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `sections` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d0c6475dc2396c17", "name": "Frontend route `digest` has no Link/navigate to it \u2014 veille-creative/src/App.jsx", "shortDescription": {"text": "Frontend route `digest` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "fullDescription": {"text": "The route is registered but no `<Link to=\u2026>` or `navigate(\u2026)` in the codebase navigates here. Either it's reachable only via direct URL (intentional), it's dead, or the link broke during a refactor."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}]}}, "automationDetails": {"id": "repobility/24195"}, "properties": {"repository": "andrianinaralison/veille-creative", "repoUrl": "https://github.com/andrianinaralison/veille-creative", "branch": "main"}, "results": [{"ruleId": "scanner-7f9ae35a79f43111", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/test-supabase.js:19"}, "properties": {"repobilityId": "32142e3e22bacbe6", "scanner": "scanner-primary", "fingerprint": "7f9ae35a79f43111", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-ef7e5f4fa16a4df8", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/test-prisma.js:12"}, "properties": {"repobilityId": "d8621cd5e25782a2", "scanner": "scanner-primary", "fingerprint": "ef7e5f4fa16a4df8", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-68d2980922b80265", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/seed-mock-data.js:26"}, "properties": {"repobilityId": "79fa0fec925f9c77", "scanner": "scanner-primary", "fingerprint": "68d2980922b80265", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-27a1dd67834d627d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/scripts/upload-thumbnails.js:37"}, "properties": {"repobilityId": "5fb48470312a0ff2", "scanner": "scanner-primary", "fingerprint": "27a1dd67834d627d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-34667642a0d2b73d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/index.js:16"}, "properties": {"repobilityId": "320f2eb194d6231c", "scanner": "scanner-primary", "fingerprint": "34667642a0d2b73d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-cebba6cb2f70615d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/enrichment.service.js:91"}, "properties": {"repobilityId": "fc67f04e8710e116", "scanner": "scanner-primary", "fingerprint": "cebba6cb2f70615d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-1d87aa7699b5d65d", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/src/services/ingestion.service.js:53"}, "properties": {"repobilityId": "65b08f9e0662d82b", "scanner": "scanner-primary", "fingerprint": "1d87aa7699b5d65d", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-761131c4db408135", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/CategoryPage.jsx:58"}, "properties": {"repobilityId": "b2a48ece769e290c", "scanner": "scanner-primary", "fingerprint": "761131c4db408135", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-741e6656f9784553", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectDetail.jsx:234"}, "properties": {"repobilityId": "9a5ea314ff18da43", "scanner": "scanner-primary", "fingerprint": "741e6656f9784553", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-34bb307287c6ec58", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/LibraryPage.jsx:240"}, "properties": {"repobilityId": "e416113ad207de34", "scanner": "scanner-primary", "fingerprint": "34bb307287c6ec58", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c17181ba63a2a286", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/ProjectsPage.jsx:44"}, "properties": {"repobilityId": "df90cf018eae2d1f", "scanner": "scanner-primary", "fingerprint": "c17181ba63a2a286", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c957b844152bbe18", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:165"}, "properties": {"repobilityId": "d072fd8a2e38cb68", "scanner": "scanner-primary", "fingerprint": "c957b844152bbe18", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-77d975575340fb87", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/SectionsAdminPage.jsx:134"}, "properties": {"repobilityId": "e9279c0bd0e4115e", "scanner": "scanner-primary", "fingerprint": "77d975575340fb87", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7efa36b76413bc1c", "level": "note", "message": {"text": "Icon-only button without accessible name \u2014 veille-creative/src/pages/admin/CurationPage.jsx:110"}, "properties": {"repobilityId": "8ce4324ca87e7139", "scanner": "scanner-primary", "fingerprint": "7efa36b76413bc1c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.button.no-label"]}}, {"ruleId": "scanner-ba72f5c6df47798f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/CurationPage.jsx:194"}, "properties": {"repobilityId": "fbdb0702aa1b6c1d", "scanner": "scanner-primary", "fingerprint": "ba72f5c6df47798f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-304e7cceb9e73aac", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/ReferencesAdminPage.jsx:174"}, "properties": {"repobilityId": "7586ba6208b8690e", "scanner": "scanner-primary", "fingerprint": "304e7cceb9e73aac", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3165f13821cc9725", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 veille-creative/src/pages/admin/DigestAdminPage.jsx:194"}, "properties": {"repobilityId": "e2d3f220832c4b85", "scanner": "scanner-primary", "fingerprint": "3165f13821cc9725", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-1fc5d583a259d3ea", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in docs/00-cadrage/business/decks/deck-180degres.html:557"}, "properties": {"repobilityId": "309d8210e1da6b0f", "scanner": "scanner-primary", "fingerprint": "1fc5d583a259d3ea", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docs/00-cadrage/business/decks/deck-180degres.html"}, "region": {"startLine": 557}}}]}, {"ruleId": "scanner-8e47d46387a7fd02", "level": "note", "message": {"text": "Very large file: veille-creative/src/pages/admin/CurationPage.jsx (1719 lines)"}, "properties": {"repobilityId": "28f866d441da87b7", "scanner": "scanner-primary", "fingerprint": "8e47d46387a7fd02", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-ea3b5e389d8c9c0f", "level": "note", "message": {"text": "Low test-to-source ratio"}, "properties": {"repobilityId": "ef7b2552cc00a375", "scanner": "scanner-primary", "fingerprint": "ea3b5e389d8c9c0f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["tests"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "224296f50224ab96", "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": "155c3d5283a44536", "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": "66f9d0ffca69cee0", "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": "cdb6ba836f16c380", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-749d4bc1bd66df5f", "level": "warning", "message": {"text": "Agent instructions exist but release-hardening basics are missing"}, "properties": {"repobilityId": "9285fede9423218a", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-f9f933452fcf93b9", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: .claude/commands/pm-probe.md"}, "properties": {"repobilityId": "2ac26147c9efd50d", "scanner": "scanner-primary", "fingerprint": "f9f933452fcf93b9", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".claude/commands/pm-probe.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-5b07939703846476", "level": "warning", "message": {"text": "Agent authority lacks a verifier contract: .claude/commands/pm-prioritize.md"}, "properties": {"repobilityId": "4f42bc3237e2124c", "scanner": "scanner-primary", "fingerprint": "5b07939703846476", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "verification", "claude_instruction"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".claude/commands/pm-prioritize.md"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d1d2387f9eb33445", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/lib/youtube.client.js:34"}, "properties": {"repobilityId": "5b34b036f0ddff0a", "scanner": "scanner-primary", "fingerprint": "d1d2387f9eb33445", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-46e46c635561a742", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 server/src/services/thumbnail.service.js:47"}, "properties": {"repobilityId": "23389bd8ccbe586a", "scanner": "scanner-primary", "fingerprint": "46e46c635561a742", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-0fe6c640c6590f4b", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/pages/CategoryPage.jsx:25"}, "properties": {"repobilityId": "2670a072f3ce2131", "scanner": "scanner-primary", "fingerprint": "0fe6c640c6590f4b", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-fe6c126df7983385", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/admin-api.js:19"}, "properties": {"repobilityId": "c501c140360c5bcf", "scanner": "scanner-primary", "fingerprint": "fe6c126df7983385", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-354b3414cf4d0305", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 veille-creative/src/lib/api.js:7"}, "properties": {"repobilityId": "dfc526adf4ccf6e5", "scanner": "scanner-primary", "fingerprint": "354b3414cf4d0305", "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"]}}, {"ruleId": "scanner-0509b90847f822b8", "level": "warning", "message": {"text": "Frontend route `/t/:token` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "f469e70c8eed27fb", "scanner": "scanner-primary", "fingerprint": "0509b90847f822b8", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-a829fddabd5e1919", "level": "warning", "message": {"text": "Frontend route `/library/section/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "88dd1be124795b06", "scanner": "scanner-primary", "fingerprint": "a829fddabd5e1919", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-dd47a6e26b6a018c", "level": "warning", "message": {"text": "Frontend route `/projects/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "c78227af5389fc7b", "scanner": "scanner-primary", "fingerprint": "dd47a6e26b6a018c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-d582a017fcdfd2dd", "level": "warning", "message": {"text": "Frontend route `/digest/:id` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "796c2f684a3ba2e7", "scanner": "scanner-primary", "fingerprint": "d582a017fcdfd2dd", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-8070c8b7e9521e8a", "level": "warning", "message": {"text": "Frontend route `/surprises` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "1ce1321c70d26943", "scanner": "scanner-primary", "fingerprint": "8070c8b7e9521e8a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-eca39b8aa2e5367c", "level": "warning", "message": {"text": "Frontend route `/admin` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "407439cfa9624827", "scanner": "scanner-primary", "fingerprint": "eca39b8aa2e5367c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-a6e068d89a0fcec3", "level": "warning", "message": {"text": "Frontend route `curation` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "d4a13f818a06f7ab", "scanner": "scanner-primary", "fingerprint": "a6e068d89a0fcec3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-090a9d87f3866733", "level": "warning", "message": {"text": "Frontend route `references` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "a08cbdd02ee848e4", "scanner": "scanner-primary", "fingerprint": "090a9d87f3866733", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-35c671736dc862f0", "level": "warning", "message": {"text": "Frontend route `sections` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "b0369e73f078d193", "scanner": "scanner-primary", "fingerprint": "35c671736dc862f0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}, {"ruleId": "scanner-d0c6475dc2396c17", "level": "warning", "message": {"text": "Frontend route `digest` has no Link/navigate to it \u2014 veille-creative/src/App.jsx"}, "properties": {"repobilityId": "2f3bbbd9af22c6ea", "scanner": "scanner-primary", "fingerprint": "d0c6475dc2396c17", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "orphan-page", "wiring"]}}]}]}