Публічний реєстр
Звіт про здоров'я програмного забезпеченнясхема 0.27.0 · метрики 1.13.0 · 2026-07-23 00:10 UTC

opendefender / OpenRisk

Unified Risk & Threat Intelligence Management Platform

Go · TypeScriptAGPL-3.0★ 14 зірок⑂ 1 форкз жовт. 2025 р.Переглянути на GitHub ↗

opendefender/OpenRisk має індекс здоров’я 63 зі 100, що відповідає смузі «Помірний». Найвищий показник — Vitality (89/100), найнижчий — Security (48/100). Останнє оновлення — сьогодні. Більшість нещодавньої роботи виконує один учасник.

63
загалом / 100
Помірний

Індекс здоров'я програмного забезпечення

Метрики згруповано у зважені категорії на шкалі 1–100. Загальна оцінка починається як їхнє середнє; коли публічні дані активують Політику юрисдикцій високого ризику, рейтинг коригується й отримує верхню межу 49 («Під ризиком»). Готовність до ШІ не входить до індексу.

63
Відмінний85-100Зразковий; відповідає практично всім перевіреним критеріям
Добрий70-84Здоровий; незначні прогалини
Помірний50-69Прийнятний, але з помітними прогалинами; рекомендовано перевірку
У зоні ризику30-49Суттєві слабкі місця; впровадження потребує обережності
Критичний1-29Серйозні проблеми (покинутий, єдиний мейнтейнер, без базової гігієни)
ЖиттєздатністьСпільнота тавпровадженняСталість таврядуванняІнженернаякістьБезпекаГотовність доШІ

Профіль оцінок

Кожна вісь — окрема категорія. Форма важить більше, ніж середнє: здоровий об'єкт заповнює всю фігуру, тоді як профіль із піками та провалами означає, що сила в одному вимірі маскує ризик в іншому.

Власність

OpenDefenderОрганізація
7 підписників2 публічні репозиторіїз лист. 2025 р.

За цим репозиторієм стоїть організація — спільна, підзвітна опіка, здатна пережити будь-якого окремого мейнтейнера.

Пакетні екосистеми

РеєстрПакетВерсіяЗавантажень / місВерсіїОстання публікація
Gogithub.com/opendefender/openriskv1.0.7-1150 днів тому

Метрики за категоріями

Життєздатність

Чи живий проєкт — чи пишеться код і чи виходять релізи?

89Відмінний · 22% загального індексу
Як обчислюється оцінка
36/36Свіжість push — останній push 0 дн. тому
18/36Ритм комітів — 26/52 тижнів із комітами
18/18Обсяг комітів — 776 комітів за останній рік
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Використані вхідні дані
commits_last_year776
human_commit_share1
days_since_last_push0
active_weeks_last_year26
Як обчислюється оцінка
27/27Випускає релізи — опубліковано 8 релізів
36/36Свіжість релізів — останній реліз 5 дн. тому
27/27Ритм релізів — реліз кожні ~37,2 дн.
0/10OpenSSF Scorecard: Signed-Releases — немає даних
Використані вхідні дані
releases_count8
latest_release_tag1.0.8
releases_from_tagsні
days_since_latest_release5
mean_days_between_releases37,2
Виключено з оцінювання (немає даних або не застосовно): OpenSSF Scorecard: Signed-Releases. Залишкові ваги перенормовано.

Спільнота та впровадження

Чи має проєкт користувачів, завантаження, увагу та влаштовані умови для контриб’юторів?

50Помірний · 18% загального індексу
Як обчислюється оцінка
18.1/60Зірки — 14 зірок
0/25Форки — 1 форків
1.7/15Спостерігачі — 3 спостерігачів
Використані вхідні дані
forks1
stars14
watchers3
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Як обчислюється оцінка
22.5/22.5README
22.5/22.5Ліцензія — визнана ліцензія (AGPL-3.0)
18/18Настанови CONTRIBUTING
13.5/13.5Кодекс поведінки
0/7.2Шаблон issue
0/6.3Шаблон PR
Використані вхідні дані
has_readmeтак
has_licenseтак
has_contributingтак
has_issue_templateні
has_code_of_conductтак
has_pull_request_templateні

Сталість та врядування

Чи переживе проєкт своїх людей — бас-фактор, реактивність, хто за ним стоїть і як супроводжуються пакети?

52Помірний · 24% загального індексу
Як обчислюється оцінка
9/54Бас-фактор — на 1 контриб’ютор(ів) припадає половина всіх комітів
0/22.5Розподіл комітів — головний контриб’ютор — автор 100% комітів
1.4/13.5Широта контриб’юторів — 1 контриб’юторів
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
Використані вхідні дані
bus_factor1
contributors_sampled1
top_contributor_share1
Як обчислюється оцінка
46.8/46.8Вирішення issue — закрито 100% issue
37.7/38.3Прийняття PR — злито 138/140 вирішених PR
0/15OpenSSF Scorecard: Code-Review — Found 0/8 approved changesets -- score normalized to 0
Використані вхідні дані
merged_prs138
open_issues0
closed_issues4
issue_closed_ratio1
closed_unmerged_prs2

Власність та опіка

41У зоні ризику
Як обчислюється оцінка
30/30Підтримка власника — у власності організації
0/20Верифікований домен
6.5/25Охоплення власника — 7 підписників у opendefender
4.9/25Послужний список — 2 публічних репозиторіїв, вік облікового запису ~0 р.
Використані вхідні дані
followers7
owner_typeOrganization
is_verified
owner_loginopendefender
public_repos2
account_age_days257
Як обчислюється оцінка
25/25Опубліковано й доступно — 1 пакет(ів) у go
35/35Свіжість публікацій — остання публікація 150 дн. тому
4/20Історія версій — 1 опублікованих версій
20/20Не застарілий — активний, не deprecated і не yanked
Використані вхідні дані
packagesgithub.com/opendefender/openrisk
ecosystemsgo
any_deprecatedні
min_days_since_publish150

Інженерна якість

Чи наявні базові інженерні практики та документація?

72Добрий · 20% загального індексу
Як обчислюється оцінка
24/24Процеси CI — 5 процес(ів) CI
24/24Наявні тести
16/16Конфігурація лінтера — .golangci.yml, eslint.config.js
0/9.6Pre-commit-хуки
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — 0 out of 8 merged PRs checked by a CI test -- score normalized to 0
Використані вхідні дані
has_ciтак
has_testsтак
has_editorconfigні
has_linter_configтак
has_precommit_configні

Документація

85Відмінний
Як обчислюється оцінка
30/30README
25/25Каталог документації
0/15Сайт документації / домашня сторінка
10/10Опис репозиторію
10/10Теми — 19 тем
10/10Wiki
Використані вхідні дані
topicsanalysis, cybersecurity, digital-forensics, digital-forensics-analysis, freesoftware, incidence-response-plan, observable, obsevability, python, security-tools, cyber-threat-intelligenece, grc, opendefender, openrisk, risk-analysis, risk-assessment, risk-management, risks, risks-score
has_wikiтак
homepage
has_readmeтак
has_docs_dirтак
has_descriptionтак

Безпека

Чи міцні видимі практики безпеки й ланцюга постачання, без непослабленої пов’язаності з юрисдикціями високого ризику?

48У зоні ризику · 16% загального індексу

Стан безпеки

48У зоні ризику
Як обчислюється оцінка
6.8/7.5Binary-Artifacts — binaries present in source code
0/7.5Branch-Protection — немає даних
0/2.5CI-Tests — 0 out of 8 merged PRs checked by a CI test -- score normalized to 0
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/8 approved changesets -- score normalized to 0
0.8/2.5Contributors — project has 1 contributing companies or organizations -- score normalized to 3
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Ліцензія — license file detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
5/5Packaging — packaging workflow detected
0.5/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 1
5/5SAST — SAST tool is run on all commits
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — немає даних
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 73 existing vulnerabilities detected
Використані вхідні дані
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4,8
Виключено з оцінювання (немає даних або не застосовно): branch_protection, signed_releases. Залишкові ваги перенормовано.

Готовність до ШІ

Наскільки репозиторій оснащений для розробки та супроводу за участі ШІ-агентів? Незалежний, експериментальний бейдж — вага 0.0, тож він подається окремо і не впливає на загальний індекс здоров'я.

80Добрий · 0% загального індексу
Як обчислюється оцінка
45/45Інструкції для агентів — CLAUDE.md, docs/Claude.md
0/15Машиночитана документація (llms.txt)
40/40Читабельна історія комітів — намір зазначено у 100 з 100 людських комітів (структурований заголовок або пояснювальний текст)
Використані вхідні дані
has_llms_txtні
legible_history_share1
agent_instruction_filesCLAUDE.md, docs/Claude.md
agent_instruction_max_bytes130 116
Як обчислюється оцінка
18/18Розгортання однією командою — Makefile
22/22Автоматизовані тести
11/11Конфігурація лінтера / форматера — .golangci.yml, eslint.config.js
11/11Статична перевірка типів — frontend/tsconfig.json
10/10Відтворюване середовище — Dockerfile, lockfile
10/10Підтверджена практика роботи з агентами — 33 з останніх 100 комітів створено агентом або з його зазначенням
0/8Автоматизоване супроводження — автоматичних оновлень залежностей не виявлено
1/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 1
Використані вхідні дані
has_nixні
has_testsтак
lockfilesgo.sum, package-lock.json
has_dockerfileтак
typed_languageтак
bootstrap_filesMakefile
has_devcontainerні
has_linter_configтак
typecheck_configsfrontend/tsconfig.json
agent_commit_share0,33
toolchain_manifestsagent/go.mod, backend/go.mod
dependency_bot_commit_share0
Як обчислюється оцінка
45/45Типізований код — Go (статично типізована)
54.9/55Керовані розміри файлів — 2/825 файлів вихідного коду понад 60 КБ
Використані вхідні дані
primary_languageGo
largest_source_bytes109 967
source_files_sampled825
oversized_source_files2
Як обчислюється оцінка
40/40Схема API (OpenAPI/GraphQL/proto) — docs/openapi.yaml
0/20Сервер MCP
0/40Придатні до запуску приклади
Використані вхідні дані
example_dirs
has_mcp_signalні
api_schema_filesdocs/openapi.yaml

Ключові факти

14зірок GitHub
1контриб'юторів
776комітів за останні 12 місяців
0днів від останнього пушу
8релізів
1бас-фактор
0відкритих issue
Go, npmпакетних екосистем

Попередження щодо збору даних

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • Could not fetch go package 'github.com/opendefender/openrisk-agent' from its registry
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Докладніше

OpenSSF Scorecard 4.8 / 10
4.8сукупно

Незалежна, не прив'язана до інструментів оцінка безпеки від відкритого проєкту OpenSSF Scorecard. Кожна перевірка винагороджує практику безпеки, а не інструмент конкретного постачальника. Перевірки, які Scorecard не зміг визначити, позначено н/д і виключено з оцінки безпеки (вони ніколи не зараховуються як нуль).Scorecard v5.5.0 · 2026-07-23 00:10 UTC

9Binary-Artifactsbinaries present in source code
н/дBranch-Protectioninternal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
0CI-Tests0 out of 8 merged PRs checked by a CI test -- score normalized to 0
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/8 approved changesets -- score normalized to 0
3Contributorsproject has 1 contributing companies or organizations -- score normalized to 3
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
10Packagingpackaging workflow detected
1Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 1
10SASTSAST tool is run on all commits
10Security-Policysecurity policy file detected
н/дSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities73 existing vulnerabilities detected
Прямі залежності 65
РеєстрПакетОбмеження версіїМаніфест
Gocloud.google.com/go/computev1.64.0backend/go.mod
Gogithub.com/Azure/azure-sdk-for-go/sdk/azcorev1.22.0backend/go.mod
Gogithub.com/Azure/azure-sdk-for-go/sdk/azidentityv1.14.0backend/go.mod
Gogithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraphv0.10.0backend/go.mod
Gogithub.com/anthropics/anthropic-sdk-gov1.56.0backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2v1.42.1backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2/configv1.32.30backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2/credentialsv1.19.29backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2/service/ec2v1.316.1backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2/service/s3v1.105.1backend/go.mod
Gogithub.com/aws/aws-sdk-go-v2/service/securityhubv1.73.1backend/go.mod
Gogithub.com/docker/dockerv28.5.2+incompatiblebackend/go.mod
Gogithub.com/go-ldap/ldap/v3v3.4.14backend/go.mod
Gogithub.com/go-pdf/fpdfv0.9.0backend/go.mod
Gogithub.com/go-playground/validator/v10v10.28.0backend/go.mod
Gogithub.com/gofiber/fiber/v2v2.52.12backend/go.mod
Gogithub.com/golang-jwt/jwt/v5v5.3.1backend/go.mod
Gogithub.com/golang-migrate/migrate/v4v4.19.1backend/go.mod
Gogithub.com/google/go-github/v66v66.0.0backend/go.mod
Gogithub.com/google/uuidv1.6.0backend/go.mod
Gogithub.com/lib/pqv1.10.9backend/go.mod
Gogithub.com/pquerna/otpv1.5.0backend/go.mod
Gogithub.com/prometheus/client_golangv1.19.0backend/go.mod
Gogithub.com/redis/go-redis/v9v9.6.3backend/go.mod
Gogithub.com/rs/zerologv1.35.0backend/go.mod
Gogithub.com/skip2/go-qrcodev0.0.0-20200617195104-da1b6568686ebackend/go.mod
Gogithub.com/stretchr/testifyv1.11.1backend/go.mod
Gogithub.com/vmware/govmomiv0.55.1backend/go.mod
Gogitlab.com/gitlab-org/api/client-gov1.46.0backend/go.mod
Gogolang.org/x/cryptov0.54.0backend/go.mod
Gogolang.org/x/oauth2v0.36.0backend/go.mod
Gogoogle.golang.org/apiv0.288.0backend/go.mod
Gogorm.io/datatypesv1.2.7backend/go.mod
Gogorm.io/driver/postgresv1.6.0backend/go.mod
Gogorm.io/driver/sqlitev1.6.0backend/go.mod
Gogorm.io/gormv1.31.1backend/go.mod
Gok8s.io/apiv0.34.2backend/go.mod
Gok8s.io/apimachineryv0.34.2backend/go.mod
Gok8s.io/client-gov0.34.2backend/go.mod
npm@hello-pangea/dnd^18.0.1frontend/package.json
npm@hookform/resolvers^5.2.2frontend/package.json
npm@tanstack/react-query^5.0.0frontend/package.json
npmaxios^1.13.2frontend/package.json
npmclsx^2.1.1frontend/package.json
npmdate-fns^4.1.0frontend/package.json
npmframer-motion^12.23.24frontend/package.json
npmleaflet^1.9.4frontend/package.json
npmlodash^4.17.21frontend/package.json
npmlucide-react^0.554.0frontend/package.json
npmreact^19.2.0frontend/package.json
npmreact-circular-progressbar^2.2.0frontend/package.json
npmreact-confetti^6.4.0frontend/package.json
npmreact-dom^19.2.0frontend/package.json
npmreact-grid-layout^1.5.2frontend/package.json
npmreact-hook-form^7.66.1frontend/package.json
npmreact-hot-toast^2.6.0frontend/package.json
npmreact-leaflet^5.0.0frontend/package.json
npmreact-router-dom^7.9.6frontend/package.json
npmreact-use^17.6.0frontend/package.json
npmrecharts^3.5.0frontend/package.json
npmsonner^2.0.7frontend/package.json
npmtailwind-merge^3.4.0frontend/package.json
npmuse-sound^5.0.0frontend/package.json
npmzod^4.1.12frontend/package.json
npmzustand^5.0.8frontend/package.json
Усі залежності не зібрано

Не вдалося зібрати розв'язаний набір залежностей для цього звіту: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Звіт у форматі JSON машиночитний
{
  "data": {
    "repo": {
      "topics": [
        "analysis",
        "cybersecurity",
        "digital-forensics",
        "digital-forensics-analysis",
        "freesoftware",
        "incidence-response-plan",
        "observable",
        "obsevability",
        "python",
        "security-tools",
        "cyber-threat-intelligenece",
        "grc",
        "opendefender",
        "openrisk",
        "risk-analysis",
        "risk-assessment",
        "risk-management",
        "risks",
        "risks-score"
      ],
      "is_fork": false,
      "size_kb": 168704,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "Go": 3190606,
        "CSS": 18971,
        "HTML": 369,
        "Shell": 28090,
        "PLpgSQL": 18676,
        "Makefile": 8308,
        "Dockerfile": 2682,
        "JavaScript": 49644,
        "TypeScript": 2151844
      },
      "pushed_at": "2026-07-22T19:17:37Z",
      "created_at": "2025-10-29T16:56:45Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-22T19:05:25Z",
      "description": "Unified Risk & Threat Intelligence Management Platform",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "AGPL-3.0",
      "default_branch": "master",
      "license_spdx_raw": "AGPL-3.0",
      "primary_language": "Go",
      "significant_languages": [
        "Go",
        "TypeScript"
      ]
    },
    "owner": {
      "blog": "https://opendefender.vercel.app/",
      "name": "OpenDefender",
      "type": "Organization",
      "login": "opendefender",
      "company": null,
      "location": null,
      "followers": 7,
      "avatar_url": "https://avatars.githubusercontent.com/u/242605006?v=4",
      "created_at": "2025-11-07T10:59:44Z",
      "is_verified": null,
      "public_repos": 2,
      "account_age_days": 257
    },
    "license": {
      "state": "standard",
      "spdx_id": "AGPL-3.0",
      "raw_spdx": "AGPL-3.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "1.0.8",
          "kind": "patch",
          "published_at": "2026-07-17T08:13:10Z"
        },
        {
          "tag": "v1.0.7",
          "kind": "patch",
          "published_at": "2026-02-22T12:14:43Z"
        },
        {
          "tag": "1.0.6",
          "kind": "patch",
          "published_at": "2026-01-23T07:32:03Z"
        },
        {
          "tag": "1.0.5",
          "kind": "patch",
          "published_at": "2025-12-06T19:13:16Z"
        },
        {
          "tag": "1.0.4",
          "kind": "patch",
          "published_at": "2025-11-22T18:04:20Z"
        },
        {
          "tag": "1.0.3",
          "kind": "patch",
          "published_at": "2025-10-30T14:55:13Z"
        },
        {
          "tag": "1.0.2",
          "kind": "patch",
          "published_at": "2025-10-30T11:02:48Z"
        },
        {
          "tag": "1.0.0",
          "kind": "major",
          "published_at": "2025-10-29T18:44:36Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "6139cc36f3b4436b4e688af2621d4b5d7879b5e8",
          "body": "Enhance security and configuration for multi-tenant isolation",
          "is_bot": false,
          "headline": "Merge pull request #147 from opendefender/audit/product-readiness",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T19:05:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2a6c97c16c04eefc81d3ed4b62fa8a70e99ed90c",
          "body": "main.go hardcoded the CORS allowlist and ignored the CORS_ORIGINS variable\nthat .env.example documents, so a deployment could not point CORS at its own\ndomain without editing source. Now reads CORS_ORIGINS when set, keeping the\nprevious dev/prod values as fallbacks.",
          "is_bot": false,
          "headline": "fix(config): honour CORS_ORIGINS env var instead of hardcoding origins",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T15:25:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "77facf783c6641a14b7a1be0de82b7e91784d33e",
          "body": "The project shipped a full rate-limiting middleware (internal/middleware/\nratelimit.go: AuthRateLimit/APIRateLimit/PublicRateLimit) but it was never\nmounted in main.go — /auth/login, /auth/register and /auth/legacy/login had\nno throttling at all, leaving them open to credential stuffing / brute forc\n[…]\nhared 5-attempts/15-min-per-IP limiter onto the credential endpoints.\nThe store is in-memory (per-instance); a Redis-backed limiter is recommended\nfor horizontal scaling (tracked in the audit report).",
          "is_bot": false,
          "headline": "fix(security): wire brute-force rate limiting onto credential endpoints",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T15:22:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cf2960fb4e5e5028071fa1714d0ea1c43b7b1efc",
          "body": "…t columns\n\nThe hand-maintained CREATE TABLE risks in risk_handler_test.go drifted again:\nmigrations 0034 (financial quantification) and 0035 (smart risk) added\ndowntime_hours / *_xaf / mitigation_effectiveness / smart_* columns that the\nGormRiskRepository INSERT now touches, but the test schema lac\n[…]\ned 400. Added the missing columns; the flow is green again.\n\nNote: this is the 3rd drift of this file — the underlying debt is the\nhand-maintained DDL. Tracked as a recommendation in the audit report.",
          "is_bot": false,
          "headline": "test(risk): re-sync TestRiskCRUDFlow sqlite DDL with financial + smar…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T14:58:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cc62aa5900ae77a89a62f6b3c5b1869cec8a67a6",
          "body": "…ss-tenant leak)\n\nAnalyticsService and DashboardDataService ran every aggregation query with\nno tenant_id filter, so the 12 endpoints under /analytics/* and /dashboard/*\nreturned risk/mitigation/framework/trend data aggregated across ALL tenants\nto any authenticated user. GetTopRisks/GetMitigationPr\n[…]\nelper that adds `WHERE tenant_id = ?`. Handlers resolve the tenant\nfrom the request context (fail-closed to uuid.Nil -> no rows). Added\nregression tests proving tenant A never sees tenant B's metrics.",
          "is_bot": false,
          "headline": "fix(security): scope analytics & dashboard aggregation by tenant (cro…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T14:47:34Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8d69eee389db330e75b960edb514f60095ed151a",
          "body": "Implement RBAC with business roles, permissions, and frontend integration",
          "is_bot": false,
          "headline": "Merge pull request #146 from opendefender/feature/rbac-business-roles",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T14:35:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "00bde3de0918090edaabe84ada94ca981ec4f514",
          "body": "…audit report\n\nAdd docs/RBAC_BUSINESS_ROLES.md: the runtime authorization model, the mapping of\nthe requested GRC profiles (Tenant Admin, RSSI, DSI, Risk Manager, Auditor,\nCompliance Officer, Internal Control, Asset Owner, Risk Owner, Security Analyst,\nExecutive, Viewer) to OpenRisk roles, the full \n[…]\ngation/landing/dashboards, the API,\narchitecture decisions, and a recap report (audit findings, work done, files,\nmigration, tests, live proof, future work). Add a concise module section to\nCLAUDE.md.",
          "is_bot": false,
          "headline": "docs(rbac): document business roles, permission matrix, dashboards & …",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:55:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ff5626436df19c962fdfc89e0f4cd5be05fce678",
          "body": "…r routes\n\nprotectedMarketplace := protected.Use(RequireRole(\"admin\",\"analyst\")) added the\nrole gate to the whole `protected` router: in Fiber, group.Use() appends\nmiddleware for every route registered AFTER it, so automation, CTI, scanner,\nvulnerabilities integrations, governance and the new RBAC b\n[…]\nle user now gets 200 on /automation/rules,\n/cti/vulnerabilities and /rbac/business-roles (was 403), keeps 403 on\n/marketplace/apps, /rbac/members and /governance/audit-events, and admin is\nunaffected.",
          "is_bot": false,
          "headline": "fix(rbac): scope marketplace role guard so it stops leaking onto late…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:47:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ba7990f5c34d9bec1adc9b42a99ff2dee66680bc",
          "body": "Add /settings/roles (admin-only, nav-gated) with two views: the business-role\ncatalog rendered as a permission matrix (what each GRC job role can do, grouped\nby domain, bilingual labels straight from the backend catalog) and a Members\ntable where a tenant admin (re)assigns a business role per member\n[…]\ng downgrading an admin to a scoped 'user'+role in one call.\nTyped service + React Query hooks (zero any); consumes GET /rbac/business-roles,\nGET /rbac/members, PUT /rbac/members/:userId/business-role.",
          "is_bot": false,
          "headline": "feat(frontend): Roles & access admin screen (matrix + member assignment)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:26:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4c95aaa36b8a3b12646532980314fb33fbe48b3d",
          "body": "Gate every sidebar/command-palette entry by the same permission the backend\nroute requires (visibleNavGroups), so each business role sees a menu coherent\nwith its job and empty groups disappear; governance/roles admin entries are\nadmin-only. After login, redirect to the business role's landing scree\n[…]\nntrol→compliance, Security Analyst→vulnerabilities, Executive→analytics. The\nsidebar footer shows the human role label (RSSI, Risk Manager, …).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): role-aware navigation and per-role landing",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:19:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fde821a262da98e4ddf7de56b9412c4de95c20c1",
          "body": "The login response's user object never carried the permission array (permissions\nlive in the JWT), so usePermissions()/hasPermission() always denied and isAdmin()\nwas always false — the client RBAC was effectively disconnected from the backend.\nAdd a dependency-free access-token decoder (lib/jwt.ts)\n[…]\noles/tenant_id + the returned\nbusiness_role onto the user at login AND on refresh, and make hasPermission()\nconsult the real set. hasRole() now matches either the org role or the business\nrole preset.",
          "is_bot": false,
          "headline": "feat(frontend): resolve real permissions from the JWT",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:19:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "242af4016c7152a56bac9acf8b6d1e8e64b85df2",
          "body": "Expose the runtime RBAC that actually drives authorization:\n- GET  /rbac/business-roles         → permission catalog + preset matrix (any member)\n- GET  /rbac/members                → tenant members with org role, business role\n                                       and resolved effective permission\n[…]\nwner) is protected from modification. Covered\nby tests: success, unknown role 400, not-found 404, cross-tenant isolation,\nroot-protected, downgrade-and-scope, invalid member role, list resolves perms.",
          "is_bot": false,
          "headline": "feat(rbac): business-role catalog + tenant member assignment API",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T13:03:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c5f962c9c8b5fd2a2c6023c86a44d279c1ceb2bc",
          "body": "The incident write routes were gated by RequireRole(\"admin\",\"analyst\"), but\n\"analyst\" is not a runtime org role (org roles are root/admin/user), so the\ngate was effectively admin-only and could never be granted to a scoped role.\nSwitch create/update/delete (and link-risk / actions) to the incidents:\n[…]\nsion family so RSSI and Security Analyst business roles can manage\nincidents while admin/root still pass via the \"*\" wildcard — consistent with\nhow risks, mitigations and compliance are already gated.",
          "is_bot": false,
          "headline": "feat(rbac): make incident writes permission-based (incidents:* family)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T12:57:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "479dce7b716af0ad06cca9805be969020776cc32",
          "body": "Add OrganizationMember.BusinessRole plus EffectivePermissions(), the single\nsource of truth for the JWT permission strings: root/admin collapse to the\nwildcard, a business-role 'user' expands to its least-privilege preset, and a\nlegacy profile still contributes its rules (union, deduplicated). Login\n[…]\n.\n\nAdds migration 0038 (nullable business_role column + index); GORM AutoMigrate\nalready provisions it. Covered by domain tests (wildcard, preset expansion,\nbare-user empty, profile+role union dedup).",
          "is_bot": false,
          "headline": "feat(rbac): resolve business roles on org members at login and refresh",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T12:56:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "310e3e7be058fb4540324349acb34b606087404b",
          "body": "Introduce a single source of truth for authorization: PermissionCatalog\n(every resource:action string the route guards check, with FR/EN labels and\ndomain groups) and 11 least-privilege business-role presets matching the GRC\njob functions the product targets — RSSI/CISO, DSI/CIO, Risk Manager, Audit\n[…]\nm between\nbackend login resolution and the API that feeds the frontend RBAC matrix.\nValidateBusinessRoles() (exercised by tests) guarantees no preset can ship a\npermission string that no route checks.",
          "is_bot": false,
          "headline": "feat(rbac): add canonical permission catalog and business role presets",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T12:47:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3506d0b1cba1e283c4e5bdc1c9727c56cd592746",
          "body": "chore(license): relicense to open-core — AGPLv3 core + commercial Enterprise Edition",
          "is_bot": false,
          "headline": "Merge pull request #145 from opendefender/chore/open-core-relicense",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T12:03:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5a96416b61f33416cd1e60cd3d0a5428a0c55cc9",
          "body": "…ition\n\nfix(mitigations): make a plan's status changeable from the board (À faire → En cours)",
          "is_bot": false,
          "headline": "Merge pull request #144 from opendefender/fix/mitigation-status-trans…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T12:00:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bd8cac5e286be3452bacd44b39363b57294f37fa",
          "body": "…erprise Edition\n\nReplaces the repository-wide BUSL-1.1 license with an open-core model:\n\n- Community Edition (the core GRC platform) → GNU AGPL v3.0 (LICENSE, verbatim\n  FSF text). The AGPL network clause keeps a competitor from offering a modified\n  core as a closed hosted service.\n- Enterprise Ed\n[…]\n0% first-party, so the relicense needs no external consent; a CLA\ngoverns future contributions to preserve the dual-licensing right. Headers are\ncomments only — go build ./... and tsc -b remain green.",
          "is_bot": false,
          "headline": "chore(license): relicense to open-core — AGPLv3 core + commercial Ent…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T11:14:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ace0a9ebc97bc27e26ec6389d429352489b8f204",
          "body": "…aire → En cours)\n\nClicking a mitigation card did nothing and there was no way to advance a plan\nacross the Kanban. Two root causes:\n\n- Backend: PATCH /mitigations/:id silently ignored `status` — the handler body\n  struct and UpdateMitigationPlanInput had no Status field. Added it, with\n  validation\n[…]\nthe backend `progress` field\n  (was reading the non-existent `progress_percentage`, always 0).\n\nLive-verified: create → PLANNED; PATCH IN_PROGRESS 200; PATCH DONE 200 progress\n100; invalid status 400.",
          "is_bot": false,
          "headline": "fix(mitigations): make a plan's status changeable from the board (À f…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T10:52:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1fcd472b5a7f05b0cdf40be4e23861273b073dbd",
          "body": "…rkflows\n\nImplement governance features: audit trail, delegation, and approval workflows",
          "is_bot": false,
          "headline": "Merge pull request #143 from opendefender/feature/governance-audit-wo…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T09:49:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5f26fc46f7fa904bdcac8952a5e635e5986fbe80",
          "body": "… approvals\n\nROADMAP: new row 14.20 \"Gouvernance = « 15. Gouvernance »\" (✅, live-proven 22/07).\nCLAUDE.md: new \"Gouvernance\" section documenting the audit-trail architecture\n(how the GORM plugin guarantees devs can't forget to log + one-line Auditable\nopt-in + secret redaction + actor attribution), \n[…]\nure (snapshotted steps, four-eyes, role eligibility, min-approvals),\nthe delegation model, and how to check permissions in code (RequirePermission /\nRequireRole middlewares, GetContext/GetUserClaims).",
          "is_bot": false,
          "headline": "docs: record Governance module (spec §15) — audit trail, delegations,…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T09:27:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f995cb87e3b6018a555a3c0b18a6595226995bf0",
          "body": "Stamp the acting identity + ip/user-agent/request-id onto the request context\n(c.UserContext) for every route under the protected group, right after auth\nresolves the session. Any repository that threads c.UserContext() into GORM —\nas the asset/compliance repos already do — now lets the audittrail p\n[…]\nem\". Value-only and additive; it never changes request handling.\n\nLive-verified: creating an asset now journals actor=admin@opendefender.io\nwith the caller IP, instead of an unattributed system event.",
          "is_bot": false,
          "headline": "feat(backend): attribute the audit actor across all authenticated routes",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T09:19:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "319184194cf96989d99478d96114c82f7a1b604f",
          "body": "…legations, workflows)\n\nA dedicated /governance screen (sidebar item, FR/EN) with four tabs, typed\nagainst the backend (zero `any`), React Query hooks, 3 UI states everywhere:\n\n- Audit trail (admin): filterable, interactive journal. Each row expands to a\n  before→after diff (changed field: old ↳ new\n[…]\nsions, status) +\n  create dialog (delegate, permissions, end date) + revoke.\n- Workflows (admin): the approval-chain builder — add/remove/reorder steps\n  (name, approver role, min-approvals) + delete.",
          "is_bot": false,
          "headline": "feat(frontend): build governance UI (audit trail, approvals inbox, de…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T08:21:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "32b377a2d7c77795f77362649ae81d127facd984",
          "body": "Wires the Governance module into the app: mounts /governance/* routes,\nregisters the four aggregates in AutoMigrate, and installs the audittrail\nGORM plugin once the tables exist.\n\nEndpoints (tenant-scoped):\n- GET  /governance/audit-events[/export]   (admin; interactive log + CSV export)\n- GET/POST \n[…]\n identity + ip/ua/request-id onto the request\ncontext (govCtx) so the Recorder and the ORM plugin attribute mutations, and\nderives approver role-eligibility + four-eyes from the JWT (approverFromCtx).",
          "is_bot": false,
          "headline": "feat(backend): expose governance HTTP API and install audit-trail plugin",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T08:14:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c145012e1a16ee86ea29ee00cea301ca831a8085",
          "body": "Governance spec §15, pillar 3. An admin configures an ApprovalWorkflow that\nbinds an (entity_type, action) pair — e.g. (risk_acceptance, accept) — to an\nordered chain of approval steps, each naming an eligible role and a\nmin-approvals count. A request then walks that chain as a state machine.\n\nThe D\n[…]\nove/reject is journaled through the AuditRecorder\n\nTests cover the full two-step chain, four-eyes, role-ineligibility, reject,\ndual-control (min 2) with double-sign rejection, conflict, and not-found.",
          "is_bot": false,
          "headline": "feat(backend): implement dynamic Maker-Checker approval workflow engine",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T08:13:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2eea8c9a54229732701518089aa382e49cd5fb7c",
          "body": "Governance spec §15, pillar 2. Lets a user lend a subset of their rights\n(or \"*\") to a colleague for a bounded window (leave/absence cover).\n\n- domain.Delegation with IsActiveAt(t): status==active AND within [start,end]\n- CreateDelegation (rejects self-delegation, empty perms, end<=start),\n  ListDel\n[…]\ner active right now — what an authz layer would OR into their perms)\n- GormDelegationRepository + audit-event repo, tenant-scoped on every query\n- create/revoke are journaled through the AuditRecorder",
          "is_bot": false,
          "headline": "feat(backend): add time-boxed delegation engine",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T08:12:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "53de77d25c2e480f55b93ef15d131463f456fc67",
          "body": "Governance spec §15, pillar 4. A GORM plugin (internal/infrastructure/\naudittrail) registers create/update/delete callbacks that automatically\nappend a domain.AuditEvent whenever a model implements the Auditable marker\ninterface — so a developer can never forget to journal a mutation. It\ncaptures Wh\n[…]\n- migration 0037 (mirrors AutoMigrate for migrations-only deploys)\n- plugin_test: proves create/update/delete capture, before→after diff,\n  secret redaction, and that tenant-less mutations are dropped",
          "is_bot": false,
          "headline": "feat(backend): add database hooks for immutable audit trail",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-22T08:10:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "269843fc1afba041b3563b337d77e959008b1ca7",
          "body": "Implement unified AI Assistant with GRC capabilities and endpoints",
          "is_bot": false,
          "headline": "Merge pull request #142 from opendefender/feature/ai-integration",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-19T09:11:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6a797280aaf312280ec6674292c0c1ad27ce0a01",
          "body": "Implement executive dashboard aggregation and frontend UI",
          "is_bot": false,
          "headline": "Merge pull request #141 from opendefender/feature/executive-dashboard",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-19T08:46:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55f7e501bb849ea8906419dd586797e7dcdf5f6e",
          "body": "…-workflow\n\nImplement SOAR workflows with event queue, notifications, and API",
          "is_bot": false,
          "headline": "Merge pull request #140 from opendefender/feature/security-automation…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-19T08:15:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b91a186f4f374f6bbbd1d9f29872d22d82163d87",
          "body": "Implement multifactor smart-risk engine with configurable weights",
          "is_bot": false,
          "headline": "Merge pull request #139 from opendefender/feature/smart-risk-calculation",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-19T08:10:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6af8a0d183f139e7f9370d02df839e815a887f97",
          "body": "- ROADMAP: Module \"10./12. IA\" 🟡 → ✅ (5 capabilities, live-proven) + detail\n  section \"M12 — IA (spec §12)\".\n- CLAUDE.md: new \"IA — Assistant GRC (spec §12)\" section documenting the unified\n  pkg/ai.Assistant architecture, the JSON-strict prompt-engineering strategy, the\n  hybrid-RAG context assembly, the 5 use cases / 6 endpoints, the frontend\n  triggers, the ANTHROPIC_API_KEY / ANTHROPIC_MODEL env vars, and the live proof.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: record AI GRC Assistant (spec §12 / ROADMAP Module 10-12)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T22:29:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b8123054d830b28efdfe6c7128bf9030e6d4e0af",
          "body": "- AiEvidenceAnalysis: inline \"Analyser (IA)\" control under each uploaded evidence\n  in the compliance control drawer (spec §12.5) — coloured documentary-compliance\n  verdict + confidence + rationale/gaps/suggestions (the \"AI analysis status\n  indicator\" the spec asks for), via POST /ai/evidence/:id/\n[…]\nch audit row (spec §12.4)\n  — executive summary / findings / recommendations / conclusion, copy-to-clipboard,\n  via POST /ai/audits/:id/report.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): \"Generate with AI\" triggers on evidence and audits",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T22:22:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3fb6d46704f2df6277d7a6f6aeb7acd418630380",
          "body": "New /ai/emerging-risks screen: paste threat-intel / news / logs, the AI extracts\ncandidate new risks (title, severity, category, suggested probability/impact,\nrationale) via POST /ai/emerging-risks. Sidebar entry + route + i18n. Real\nreplacement for the fully-mocked AIRiskInsights concept.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): emerging-risk detection page (spec §12.2)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T22:09:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f6fe275a57e4c5a2e512a625d21a588e280a1a77",
          "body": "Wires the previously \"coming soon\" IA tab of the live risk drawer to\nPOST /ai/risks/:id/treatment-plan (spec §12.1): a \"Générer avec l'IA\" button\nproduces a risk synthesis, a recommended strategy chip, an ordered action plan\n(priority-coloured), the rationale, and the generated_by provenance.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): \"Generate with AI\" treatment plan in the risk drawer",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T22:02:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a5beecd8c84a32bb66cf7b3e6245328090b691bf",
          "body": "… service/hooks\n\n- aiService.ts + useAi.ts: typed client (zero any) for all six /ai/* endpoints.\n- AiAdvisor.tsx: the /recommendations chat now calls /ai/assistant/query for\n  real — answers are grounded in the tenant's own GRC data via backend RAG and\n  render the cited sources as chips. Provenance badge shows Claude vs local\n  template mode, with a live \"analysing…\" state. Removes the hardcoded\n  keyword-reply mock.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): live AI assistant chat (replaces the mock) + typed AI…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T21:54:38Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e526cfad431a00ecbba78e78ac7d8fb240714285",
          "body": "…t API\n\nThe retrieved snippets are returned by /ai/assistant/query; give them\nsnake_case JSON keys (kind/ref/title/detail) so the typed frontend client\nmaps cleanly.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor(backend): json tags on KnowledgeSnippet for a clean assistan…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T21:50:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "096db9201ac10c37e04df8c0d277cfd5cf6a0452",
          "body": "…ases\n\n- AIHandler with 6 routes: GET /ai/status, POST /ai/assistant/query,\n  POST /ai/emerging-risks, POST /ai/risks/:id/treatment-plan,\n  POST /ai/audits/:id/report, POST /ai/evidence/:id/analyze.\n- Wired in main.go, composing the existing tenant-scoped repos (risk, asset,\n  compliance, vulnerabil\n[…]\nte).\n- Advisory (non-mutating) endpoints guarded by risks:read / compliance:read;\n  locale resolved from body or ?locale, defaulting to French.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): expose /ai/* endpoints and wire the AI assistant use c…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T21:18:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "091f74ab3c5cb9e5fabeb027db965d9f37656cd2",
          "body": "…ks, Q&A, audit report, evidence analysis)\n\nTenant-scoped use cases that assemble GRC context and hand it to the\npkg/ai.Assistant, each with a deterministic template fallback on any LLM error:\n\n- SuggestTreatmentPlan: risk + linked asset context → synthesis + plan.\n- DetectEmergingRisks: free text →\n[…]\n and nil-safe; provider (model or \"template\") is returned for\nprovenance. 10 use-case tests with mocks (Success/NotFound/Validation/retrieval).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): AI application use cases (treatment plan, emerging ris…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T21:05:17Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "faccf57ab06b2ddb27942cefa52df1ecbe1b0bda",
          "body": "… Claude + template fallback\n\nGeneralises the board-report Advisor pattern into a unified pkg/ai.Assistant\ninterface covering the five spec §12 capabilities: risk treatment plans,\nemerging-risk detection, natural-language Q&A (RAG), audit-report generation,\nand evidence document analysis.\n\n- ClaudeA\n[…]\ne /ai/status probe.\n- Pure DTOs (no domain/GORM imports); context assembly lives in the app layer.\n- 5 table-driven tests on the template path.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): unified AI Assistant service (5 GRC capabilities) with…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T17:07:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "50b1aec445dd30221c23bacb2fe39526f893ed1c",
          "body": "Documents the consolidated executive dashboard: the GET /analytics/executive\naggregation use case, the deterministic cyber-score formula, the reused\ntenant-scoped sources, the Recharts frontend widgets, and the live proof.\nNotes the honest remainders (sparse risk-history trend, time filters as\nnext iteration, pre-existing TestRiskCRUDFlow failure).",
          "is_bot": false,
          "headline": "docs: record Executive Dashboard (spec §11 / ROADMAP Module 8)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T16:33:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d7d77acaab07cb2c7a5243721230c542a671eb14",
          "body": "…op-10, trends, compliance)\n\nReplaces the fixture-only AnalyticsCiso preview with a fully data-driven\nexecutive board consuming the single /analytics/executive aggregation\n(no fixtures). Widgets (Recharts + dc.html tokens, light+dark):\n- cyber-score SVG gauge (A-F grade + weighted component bars)\n- \n[…]\n by severity\nCharts follow the dataviz rules: reserved status colours with legends,\none hue for magnitude, ink tokens for text, one axis per chart. Routed\nat /analytics; old AnalyticsCiso.tsx removed.",
          "is_bot": false,
          "headline": "feat(frontend): live executive dashboard UI (cyber score, exposure, t…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T16:24:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3f646f776409a5ff66ac2a590a8c649c1ad50b7f",
          "body": "Typed client (zero any) for GET /analytics/executive mirroring\ninternal/application/dashboard.ExecutiveDashboard, plus a React Query\nhook that refetches every 60s so a board left open stays current.",
          "is_bot": false,
          "headline": "feat(frontend): executive dashboard typed service + hook",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T16:24:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "07965168d84c86d068395b072df7b38e85fca945",
          "body": "…cutive\n\nWires the aggregation use case behind GET /analytics/executive\n(risks:read, tenant-scoped via mwCtx.OrganizationID). A thin adapter in\npackage main maps the legacy incident service onto the dashboard's\nIncidentSource port, keeping the service decoupled from the application\nlayer; vuln repo, gap-analysis use case and financial-summary use case\nsatisfy their ports directly.",
          "is_bot": false,
          "headline": "feat(backend): expose executive dashboard endpoint GET /analytics/exe…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T16:13:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f6843498f7474561e7d7e6f4a008748f33a8069b",
          "body": "Adds a single tenant-scoped aggregation for the executive dashboard\n(spec §11) that consolidates risk, financial, compliance, vulnerability\nand incident posture so the frontend makes one request instead of a\ndozen. Composes the existing tenant-scoped sources; every source port is\noptional and nil-sa\n[…]\nrisk_histories joined to risks,\n  current month anchored to the live register) + TopRisksByScore.\n- IncidentService.GetIncidentAnalytics (open/critical volume, MTTR,\n  resolution rate, monthly trend).",
          "is_bot": false,
          "headline": "feat(backend): executive dashboard aggregation use case + cyber score",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T16:12:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "91a823f6e4c572a2051f70ef7918a305c773d769",
          "body": "…tification\n\nImplement financial quantification features and endpoints",
          "is_bot": false,
          "headline": "Merge pull request #138 from opendefender/feature/financial-risk-quan…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T15:56:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "467d7ac40ea50cdfe99e067d85d222883ecda0f5",
          "body": "…ement-connectors\n\nImplement vulnerability integration and ticketing configurations",
          "is_bot": false,
          "headline": "Merge pull request #137 from opendefender/feature/vulnerability-manag…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T15:40:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2803f90c65b6c51afc0827acab11b761ffcc7fd7",
          "body": "- ROADMAP: new row 14.19 (event engine + action chain + SLA/escalation/auto-close\n  + Slack/Teams connectors, live-proven end-to-end 18/07).\n- CLAUDE.md: new « Security Automation / SOAR » section documenting the event\n  engine, background-task lifecycle (AutomationWorker + SLAMonitor), how to\n  configure/add an alert channel, and the SLA breach + escalation calculation.",
          "is_bot": false,
          "headline": "docs: record Security Automation / SOAR (spec §10 / ROADMAP 14.19)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T12:41:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5a25b2c108fd31be39820f385a9cc2daa444606b",
          "body": "…els)\n\nRead the initial tab from the URL query so the SLA dashboard, execution history\nand channel config are directly linkable. Live-verified headless: the rules view\nrenders each rule's trigger->action chain, and the SLA view shows the escalation\nstate + budget progress bars.",
          "is_bot": false,
          "headline": "feat(frontend): deep-linkable automation tabs (?tab=sla|history|chann…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T12:35:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0df4405d9443f655c763da6772d6e2c62ad8a0e0",
          "body": "New features/automation module (typed service + React Query hooks, zero any):\n- AutomationPage with four views:\n  * Rules — the workflow builder: each rule renders its trigger → action chain\n    as a visual pipeline, with enable/disable, dry-run test, edit and delete.\n  * Live SLA — the remediation \n[…]\n provider), and an SLA policy block.\n- Sidebar entry (Security group), route /automation, FR/EN strings.\n- Write actions gated by automation:write; channels save admin-only.\ntsc -b + vite build green.",
          "is_bot": false,
          "headline": "feat(frontend): automation rules builder + live SLA dashboard",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T12:18:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1ed79563e945b5c51b6599b20b8782d324611f05",
          "body": "GORM's Pluck requires a slice destination; plucking a member's user_id into a\nscalar uuid.UUID silently returned nothing, so assign_owner failed to resolve\nthe admin/manager role. Fetch into a []uuid.UUID and take the first. Proven live:\nthe full chain (create_risk → assign_owner → notify → start_sla) now succeeds.",
          "is_bot": false,
          "headline": "fix(backend): resolve assign_owner target via slice Pluck",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:59:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "06c9c3f328dc2afb90fd3f8e562361ea90e23e0f",
          "body": "- engine: condition matching (severity/CVSS/KEV/tier gates), full action chain\n  (notify + ticket + start_sla), condition-skip, create-risk chaining, missing\n  ports degrade to skipped (never fail), and the dry-run RunRuleByID path.\n- SLA: escalation sweep (only escalates past the window, notifies, \n[…]\n no-op when unresolved, at-risk\n  stats, and MinutesFor severity fallback.\n- rules: create validation (bad trigger / no actions / start_sla without budget)\n  and tenant-scoped update/delete isolation.",
          "is_bot": false,
          "headline": "test(backend): cover SOAR engine, SLA sweeps and rule validation",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:42:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "160e6feef6192f2340b024179b95fcc9d128e145",
          "body": "- handler.AutomationHandler: rule CRUD + dry-run test, execution audit trail,\n  SLA dashboard (open trackers + stats), and per-tenant alert-channel config.\n- Routes under /automation/* (RBAC: automation:read/write, channels admin-only);\n  static sub-paths registered before /:id (Fiber routing gotcha\n[…]\nLAMonitor started as background goroutines.\n- vuln ingest now publishes vulnerability.detected on a newly created finding,\n  firing the engine's vulnerability_detected trigger (the headline scenario).",
          "is_bot": false,
          "headline": "feat(backend): expose automation API + wire SOAR engine into the app",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:37:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ec54349cedac6b8a2bc437989d95d447f4f08bce",
          "body": "…LA monitor\n\nThe orchestration core (spec §10):\n- application/automation.Engine — matches a tenant's enabled rules against a\n  normalised TriggerContext and runs the ordered action chain (scan → create\n  risk → assign → ticket → notify → start SLA → resolve/close). Every action\n  port is optional an\n[…]\n workers.SLAMonitor — cadence scheduler escalating overdue remediations and\n  auto-closing resolved ones.\n- per-tenant AutomationChannelConfig (Slack/Teams/email) + rule/execution/SLA\n  CRUD services.",
          "is_bot": false,
          "headline": "feat(backend): implement event-driven SOAR engine, action chain and S…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:26:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8da9127691b96bd207d7eb59843d9dcb14760f1a",
          "body": "…ents\n\nDependency-free chat-ops posters (pkg/notify/chatops.go) that make REAL HTTP\nPOSTs to Slack (attachment format) and Microsoft Teams (MessageCard format)\nincoming webhooks. Teams is the new connector; both share a severity-coloured\nChatMessage with facts + an optional deep link. Empty URL or non-2xx returns a\nreal error, never a silent success. Unit-tested via httptest.",
          "is_bot": false,
          "headline": "feat(backend): add Slack and Microsoft Teams webhook notification cli…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:04:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "097c85a610ae463819af9c29d1725ac5f5f0d4c9",
          "body": "Add the tenant-scoped data model for the Security Automation engine (spec §10):\n- domain.AutomationRule    — trigger + conditions + ordered action chain + SLA policy\n- domain.AutomationExecution — audit record of each rule firing (jsonb steps)\n- domain.SLATracker        — live resolution countdown w\n[…]\ns (tenant-scoped on every query)\n- migration 0036 + AutoMigrate wiring\n- new Redis event channel vulnerability.detected (+ payload) that the engine\n  will consume as the vulnerability_detected trigger",
          "is_bot": false,
          "headline": "feat(backend): set up event queue and database schema for SOAR workflows",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T11:02:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0a79a2fdaa0dfc1e4b08144d39fc0962dc3ab6aa",
          "body": "Marks « 8. Calcul de risque intelligent » done in ROADMAP.md and documents\nthe multifactor engine in CLAUDE.md: the exact SmartScore formula and the\neight factors' normalisation, the risk_scoring_weights DB structure + Risk\nsmart-score columns (migration 0035), and how the configurable per-tenant\nweights are applied (relative → normalised → engine), plus the endpoints\nand frontend surface.",
          "is_bot": false,
          "headline": "docs: record smart risk calculation (spec §8 / ROADMAP 14.5b)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T10:12:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "455c0c9fa8c1832c0f4d64fdf239988d51025623",
          "body": "… nil\n\nRisks created with asset_ids populate the risk_assets join (preloaded by\nGetByID), not the single AssetID pointer. Without a fallback the asset-derived\nfactors (business criticality, vulnerabilities, incident history) never fired\nfor manually-created risks. Now falls back to the first linked asset. Proven\nlive: business criticality reads the asset's CRITICAL 3.0 factor, and a KEV\nLog4Shell finding on the asset drives the score to 84/critical.",
          "is_bot": false,
          "headline": "fix(backend): resolve smart-score asset via many2many when AssetID is…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T10:07:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ca3edb9d914c70a0b634303077ae6911e506cb64",
          "body": "Smart Risk Calculation UI (spec §8), zero `any`:\n\n- smartScoreService + useSmartScore hooks (typed, mirror pkg/scoring's\n  SmartResult/FactorScore + domain.RiskScoringWeights).\n- SmartRiskRadar: a Recharts RadarChart of the eight factor contributions\n  plus a ranked per-factor breakdown (contributio\n[…]\ne\", Reset-to-defaults and Save\n  (read-only for non-admins). i18n FR/EN throughout.\n\nThe classic Score Engine \"Score\" tab (P × I × AC) is left in place — the smart\nscore is an additional, richer view.",
          "is_bot": false,
          "headline": "feat(frontend): radar chart for risk breakdown + weight config screen",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:51:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4a335c166be7d874a6b2e9799352ddb6a458714f",
          "body": "Adds the four /risks/{id}/smart-score, /smart-score/simulate and\n/risk-scoring/weights paths (tag \"Smart Risk Calculation\") plus the\nSmartRiskScore / SmartFactorScore / FactorWeightsInput / RiskScoringWeights\nschemas (spec §8).",
          "is_bot": false,
          "headline": "docs(api): document smart risk calculation endpoints in OpenAPI",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:37:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "85c07bc8e61256e94d2121eb2869cae3036ba5e1",
          "body": "Adds SmartScoreHandler + four routes under the protected group:\n  GET  /risks/:id/smart-score            multifactor score + breakdown (risks:read)\n  POST /risks/:id/smart-score/simulate   preview with supplied weights (risks:read)\n  GET  /risk-scoring/weights             tenant factor weights (risk\n[…]\ner, CRQ quantifier, write-back\npersister) and registers domain.RiskScoringWeights in AutoMigrate. The\nper-risk GET caches the computed score on the risk. Classic Score Engine\nroutes/columns untouched.",
          "is_bot": false,
          "headline": "feat(backend): smart-score handler, routes + AutoMigrate wiring",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:33:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "70dfc3658d0b8b1aff6770e06c52db0269f0f388",
          "body": "…ounter\n\nApplication layer for the multifactor engine:\n\n- ComputeSmartScoreUseCase assembles the eight-factor SmartInput for a risk\n  from the risk, its asset, the vulnerability register, compliance posture,\n  incident history and the CRQ model, then runs pkg/scoring.ComputeSmart with\n  the tenant's\n[…]\nitical+exposed+KEV → high score, exploitability\nmaxed, persisted), not-found, graceful degradation with no optional deps,\npreview weights, default weights, update validation (all-zero / out-of-range).",
          "is_bot": false,
          "headline": "feat(backend): smart-score use cases, weights repository + incident c…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:28:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8dd213a2f294c7038ef0ca0a38e41d7d93f9d29e",
          "body": "Adds the risk_scoring_weights table (one row per tenant, unique index,\neight factor-weight columns seeded with the engine defaults) and the\nsmart-score columns on risks (smart_score 0–100, smart_level, smart_factors\njsonb breakdown, smart_computed_at) with a supporting index. AutoMigrate\nalready creates these; the SQL migration keeps a migrations-only deploy\nself-sufficient. Classic Score Engine columns are untouched (additive).",
          "is_bot": false,
          "headline": "feat(database): migration 0035 for smart risk scoring",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:16:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "54b2a50aa8cf79512767a07363daf4d1aeaadaf2",
          "body": "Adds domain.RiskScoringWeights — one tenant-scoped row (unique index on\ntenant_id) holding the eight factor weights of the smart-risk model, with\nDefaultRiskScoringWeights(), ToFactorWeights()/ApplyFactorWeights() mapping\nto pkg/scoring, Validate() ([0,1] each, at least one positive) and a\nRiskScori\n[…]\n, SmartFactors (jsonb breakdown snapshot) and SmartComputedAt —\nso the register can sort/badge on the multifactor score. The classic\nProbability/Impact/Score fields and the Score Engine are untouched.",
          "is_bot": false,
          "headline": "feat(domain): add configurable risk factor weights + smart-score columns",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:15:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "52120a525dfbdf52ad1477dd5129b841ac0b4334",
          "body": "Adds pkg/scoring/smart.go — a pure, deterministic engine (stdlib only, no\nI/O, mirrors pkg/vulnprio) that blends the eight factors of spec §8 into a\nsingle 0–100 smart score with CONFIGURABLE per-factor weights:\n\n 1. business criticality   5. incident history\n 2. internet exposure      6. exploitabi\n[…]\n. 10 unit tests\ncover zero/worst-case saturation, weight normalisation, mature-controls\nlowering risk, KEV driving exploitability, financial scaling and that the\nfactor contributions sum to the score.",
          "is_bot": false,
          "headline": "feat(scoring): implement pure multifactor smart-risk engine",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T09:11:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bb256383fe206e666d70862b633c3c2c42013253",
          "body": "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: ignore stray backend build binaries (server, orserver)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-18T05:13:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "02835296240bb294005b6343b9d7864a1bf8767d",
          "body": "Marks 14.5 CRQ FAIR as done (= \"9. Quantification financière\"), documents the\nexact formulas (composed SLE, downtime cost, PERT worst/average loss, ROSI), the\ndata flow risk→money, and the module architecture. CLAUDE.md sprint item 32.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: record financial risk quantification (spec §9 / ROADMAP 14.5)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T22:30:14Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d4c0b9dddc0ea683da31363397c8675e0ccdeb8d",
          "body": "Adds paths /risks/{id}/financial, /risks/{id}/simulate, /analytics/financial and\nschemas Money, FinancialAssessment, SimulateFinancialInput, CriticalityBucket,\nTopRiskFinancial, FinancialSummary (spec §9). YAML validated.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(api): document financial quantification endpoints in OpenAPI",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T22:25:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e338e5acbbc2d228a840e27c111de2e40479a537",
          "body": "…stment simulator\n\nAdds features/financial:\n- financialService.ts + useFinancial.ts (typed, zero any) for the 3 endpoints\n- FinancialDashboard.tsx (route /analytics/financial, sidebar \"Quantification\n  financière\"): KPI tiles (portfolio ALE, worst-case, remediation budget,\n  portfolio ROSI), a Recha\n[…]\nGET /risks/:id/financial) plus editable drivers (downtime, fines, data loss,\nremediation cost, effectiveness slider). Risk types (service + store) and i18n\n(FR/EN) extended. tsc -b + vite build green.",
          "is_bot": false,
          "headline": "feat(frontend): CFO/CISO financial dashboard + full CRQ drawer + inve…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T21:59:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f9921ee7eebb937e4802221cea8a7e01791ff0c3",
          "body": "…case model\n\nTwo fixes found by live verification:\n- ListRisksForFinancial used a hand-written SELECT with \"sle_xaf\", but GORM\n  derives the column as \"slexaf\" (all-caps SLEXAF collapses) → 500. Load the\n  full model instead so GORM maps every column correctly, like GetByID/List.\n- Annualized worst/\n[…]\nal for explicit/composed risks, and a\n  sensible envelope (worst ≥ ALE) for reference risks.\n\nLive-verified: per-risk assessment, simulator and /analytics/financial all\nreturn exact, coherent figures.",
          "is_bot": false,
          "headline": "fix(backend): correct financial aggregation column mapping and worst-…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T19:51:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6a2bdfbec8e2184ce7dc0251441d22fe04522ab4",
          "body": "…nd CFO dashboard\n\nAdds three read endpoints backed by pkg/crq's Assess:\n- GET  /risks/:id/financial  → full FinancialAssessment for one risk\n- POST /risks/:id/simulate   → what-if assessment with per-field overrides,\n                               non-persisting (investment-scenario simulator)\n- GE\n[…]\ncial (narrow SELECT, tenant-scoped, off the\ndomain port so mocks stay valid) via a narrow FinancialRiskLister port. All\nguarded by risks:read. 3 use-case tests (aggregation/ROSI, empty, lister error).",
          "is_bot": false,
          "headline": "feat(backend): financial endpoints — per-risk assessment, simulator a…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T19:23:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5ec3b97b9cc2cfd21fea0858f08a1378142a290a",
          "body": "…pdate\n\nThreads the 7 new financial inputs (downtime hours + hourly cost, data loss,\nfines, other direct cost, remediation cost, mitigation effectiveness) from the\nCreate/Update risk DTOs through the use cases onto the Risk entity. Validation:\nnon-negative amounts, effectiveness ∈ [0,1]. Partial-update semantics preserved\n(nil = leave unchanged).",
          "is_bot": false,
          "headline": "feat(backend): wire financial-quantification drivers through create/u…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T18:44:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14460ec835dcdedbc3e9f581c2ab1deea901a306",
          "body": "Adds downtime_hours, hourly_downtime_cost_xaf, data_loss_cost_xaf, fines_xaf,\nother_direct_cost_xaf, remediation_cost_xaf and mitigation_effectiveness to the\nRisk entity (domain + migration 0034). All nullable XAF amounts; an unquantified\nrisk falls back to the reference model. Risk is already in AutoMigrate.",
          "is_bot": false,
          "headline": "feat(database): add financial-quantification driver columns to risks",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T16:05:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "788af1251e2c6ca15792f42754e19e19d9646c7d",
          "body": "…vg loss and ROSI\n\nAdds pkg/crq/financial.go — a pure, deterministic financial model on top of the\nexisting ALE = SLE × ARO core:\n- DowntimeCostXAF = downtime hours × hourly cost\n- composed SLE = downtime + fines + data loss + other direct cost (spec §9)\n- worst/average single-event loss via a trian\n[…]\nion to the reference band when inputs are absent, same as\nQuantify. 6 new tests (downtime, ROSI incl. negative/undefined, composed vs\nexplicit SLE, loss band, reference fallback, effectiveness clamp).",
          "is_bot": false,
          "headline": "feat(backend): extend CRQ engine with downtime, composed SLE, worst/a…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T16:03:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1c24d6aad1d01bca179c6c620c2ad5c6a7aa1802",
          "body": "Add 7 auto-discovery connectors for various platforms",
          "is_bot": false,
          "headline": "Merge pull request #136 from opendefender/feature/auto-asset-discovery",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T13:40:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8f17a76908313b35c39932491d678ea47e870fac",
          "body": "… 6.5)\n\nDocument the integration config screen (encrypted creds + inbound webhooks),\nreal REST live-pull (Defender/CrowdStrike/Nessus/Qualys/Azure + honest OpenVAS/\nAWS seams), CTI KEV/CVSS enrichment on ingest, auto-create-risk from P1/KEV, and\nJira/ServiceNow auto-ticketing — with the live-proof (creds never returned,\nwebhook 202/401/400, real Qualys 401 recorded, encrypted-at-rest, no fake ticket).",
          "is_bot": false,
          "headline": "docs: record the 5 vulnerability-management deltas (item 31 / ROADMAP…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T13:04:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6fbf3c8b5c6e3221042ec5d54678fc0f6283f483",
          "body": "…webhooks)\n\nReplace the read-only ConnectorsPanel with a full config screen (IntegrationsPanel):\nper-source credential forms (write-only — existing secrets show as configured and\nare preserved unless re-typed), base URL, inbound webhook URL with copy +\nregenerate, live-pull toggle + schedule + Pull \n[…]\ns\n(zero any). The vulnerability drawer now shows the opened ITSM ticket link (or an\nOpen-a-ticket button) and flags a linked auto-created risk.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): vulnerability integrations config screen (API keys + …",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:39:13Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7a3a01629840bf8237fb1fb8dea71b3a2c76ce23",
          "body": "Add pkg/ticketing: a domain-free provider package with REAL REST clients for\nJira Cloud (POST /rest/api/2/issue, Basic email+token → SEC-42 + /browse URL)\nand ServiceNow (POST /api/now/table/incident, Basic auth → INC number + nav URL);\nabsent/wrong creds surface the tool's real error, never a fabri\n[…]\ngured). Ticket ref linked onto the row.\nTests: auto-open on KEV, skip when disabled, manual success/not-configured/\nalready-ticketed/not-found.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): Jira/ServiceNow ticketing + auto-ticket + manual endpoint",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:30:40Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4d3e5a0c001d18fd7c69a1203650c95c07e056ec",
          "body": "IngestUseCase gains an optional RiskProposer seam (WithRiskProposer) triggered,\nopt-in per integration (IngestInput.AutoCreateRisk, propagated from the webhook,\nlive-pull and manual-import paths), when a finding is P1 or CISA-KEV AND\nattributed to an asset. The created risk is linked back onto the v\n[…]\ndempotent by (tenant, asset, cve|name) so re-scans\nnever duplicate. Tests: KEV-on-asset creates+links, disabled/no-asset/low-priority\nall skip.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): auto-create risk from P1/KEV vulnerabilities (idempotent)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:23:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1600a80050bd2b2dc9194eb52ad1ea295154e1d4",
          "body": "…TI feed\n\nIngestUseCase gains an optional CTIEnricher seam (WithCTIEnricher): every\ningested finding with a CVE is cross-referenced against cti_vulnerabilities\n(NVD + CISA-KEV sync) BEFORE prioritisation. KEV is OR-ed in (never downgraded)\nand implies exploited-in-the-wild; CVSS/severity backfill on\n[…]\nrrent CTI feed has no EPSS; a future feed lights it up with no caller change).\nWired in main.go via a CTIRepoEnricher over the shared CTI repo.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): enrich ingested findings with CISA-KEV/CVSS from the C…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:17:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f81e01c74840210694f21064089f121fccd44f61",
          "body": "Add internal/vulnscan/livepull: a per-source Puller registry that makes REAL\nauthenticated HTTP calls and returns findings in each tool's native shape, which\nthen flow through the existing normalisers → prioritisation → upsert.\n\nReal pullers (httptest-proven mechanisms): Microsoft Defender for Endpo\n[…]\nposed at\nPOST /vulnerabilities/integrations/:id/pull. LivePullScheduler polls due\nintegrations (schedule_minutes) behind VULN_LIVEPULL_ENABLED.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): live-pull framework + real REST pullers + scheduler",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:15:32Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "08fca7c39212929fc8da192fea08296c483ef0dd",
          "body": "Scanners can now push findings automatically to\nPOST /api/v1/vulnerabilities/webhook/:source, authenticated by the integration's\nopaque webhook token (query ?token=, X-Webhook-Token, or Bearer) — no user JWT.\nMounted on `app` before the /api/v1 JWT gate (same pattern as scanner agents);\nthe token ca\n[…]\nies|\nitems} wrapper, or a single object. Unknown/disabled tokens get a uniform 401.\nTest: parseWebhookFindings across shapes + malformed input.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): token-authenticated webhook ingestion endpoint",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:07:56Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bcc3e5108439129f1e7dc90be02bc7b48d10cf49",
          "body": "… returned)\n\nAdd the connector-configuration use cases (Save/List/Get/Delete integration,\nSave/Get/Delete ticketing) behind a CredentialCipher seam satisfied by the\nscanner's AES-256-GCM cipher, plus the Fiber handler and routes under\n/vulnerabilities/integrations and /vulnerabilities/ticketing (gua\n[…]\nk token is minted on demand.\nTests: save success/unsupported-source/preserve-creds, cross-tenant NotFound,\nticketing requires-provider/success.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): integration + ticketing config CRUD (credentials never…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T12:05:44Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8d4b8cc8615077e7c3157b1f0029b16e1f097c4c",
          "body": "…ed creds)\n\nAdd VulnIntegration (per-source scanner connector config: encrypted API\ncredentials, base URL, live-pull schedule, inbound webhook token, auto-risk /\nauto-ticket toggles) and VulnTicketingConfig (tenant ITSM config) domain models,\ntheir tenant-scoped Gorm repository, and the cross-module\n[…]\n. Credentials are AES-256-GCM at\nrest and never serialised (json:\"-\"); only HasCredentials is exposed. Wired\ninto AutoMigrate + migration 0033.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(backend): vulnerability integration config model + repo (encrypt…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T11:59:32Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e9e7b06c7086068575f8ad11691a7963e3d58039",
          "body": "ROADMAP: extend the Infrastructure Scanner row with the Kubernetes/Docker/\nVMware/Active Directory/M365/GitHub/GitLab connectors + live proof. CLAUDE.md:\nsprint item #30 with the diagnostic (engine/cloud/on-prem/CMDB/scheduling\nalready done; the 7 connectors were the gap), the plugin architecture, and the\nlive GitHub-401 proof.",
          "is_bot": false,
          "headline": "docs: record the 7 new auto-discovery connectors (Module 6)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T11:32:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9d41345679ba98bcfb0d5283b532d1a79a648b02",
          "body": "The HTTP handler's createScanConfigInput had a `oneof=aws azure gcp nmap agent`\ntag that rejected the new providers with a 400 before the use case ran (found\nlive). Widen it to include kubernetes/docker/vmware/active_directory/m365/\ngithub/gitlab, matching domain.ScannerProvider.Valid().",
          "is_bot": false,
          "headline": "fix(scanner): allow the 7 new providers in the create-config validator",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T11:29:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f463f4f5dbbe6429557c667f786fbac5887c122a",
          "body": "…nsole\n\n- scannerService: extend the ScannerProvider union with kubernetes/docker/\n  vmware/active_directory/m365/github/gitlab.\n- scannerMeta: provider cards (icon/color/category) + per-provider credential\n  field definitions (endpoint + secrets, mirroring cloud.go's required keys) +\n  SCOPE_HINTS \n[…]\n show\n  the scope field only when a hint exists.\n- InfrastructurePage: add the 7 provider cards to the picker grid.\n\ntsc -b + vite build green.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): surface the 7 auto-discovery providers in the scan co…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T11:22:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "92e46ae96a309b6a424d4eaed1abf34a611992dd",
          "body": "Talk to a cluster's API server with a ServiceAccount bearer token (optional CA)\nand enumerate Nodes (Server assets, OS image → CPE, internal IP) and Pods\n(Container assets, image → CPE, namespace as environment), flagging pods that\nrun a privileged container (high). The enumeration is split behind\nk\n[…]\ncial client-go fake\nclientset. Registered. client-go pinned to v0.34.2 to keep the module's go\ndirective at 1.25 (v0.36 would bump it to 1.26).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real Kubernetes discovery connector (client-go)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T11:16:51Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ca5782716a12fac626c0f0595b99bb118514de69",
          "body": "Log in to a vCenter/ESXi endpoint and enumerate virtual machines via a\nContainerView (VM assets, guest OS → CPE, IP/hostname from guest info), flagging\nVMs whose VMware Tools are missing (medium) or out of date (low). The\nenumeration is split behind a *vim25.Client so it is exercised end-to-end\nagainst govmomi's bundled vCenter simulator (vcsim) — a real, offline\nintegration test. Registered.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real VMware vCenter discovery connector (govmomi)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:46:31Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0b9a5f3ae9378966a8d01047ab90897359e96729",
          "body": "Connect to a Docker host (tcp:// with optional in-memory PEM mTLS, or a unix\nsocket) and enumerate containers (Container assets, image→CPE) and images,\nflagging containers attached to the host network namespace. Container/image\nnormalisation is pure and unit-tested on the SDK summary types (no daemon).\nRegistered.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real Docker discovery connector (Docker Engine SDK)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:39:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f27cc03da026651e9bab3d2a75b99b0d2ac7a2bc",
          "body": "…tity)\n\nAuthenticate an Entra ID app registration via azidentity client-credentials\n(already vendored — no msgraph SDK bloat) and enumerate users (Identity assets)\nand managed devices (Workstation assets) over Microsoft Graph REST, flagging\nIntune-managed non-compliant devices. Graph pagination/norm\n[…]\n behind\nan httpDoer interface and is unit-tested against an httptest Graph endpoint;\ntoken acquisition uses the official Azure SDK. Registered.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real Microsoft 365 discovery connector (Graph + aziden…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:31:26Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "476cec3b70337bbf2c3f0b663243a846e640dadf",
          "body": "Bind to a domain controller and enumerate computer objects (Server/Workstation\nassets, OS→CPE) and person objects (Identity assets), with hygiene findings for\nend-of-life Windows (high) and never-expiring passwords (low). Search/normalise\nlogic sits behind a minimal ldapSearcher interface (real *ldap.Conn wrapped for\nserver-side paging) so it is unit-tested with a fake directory — no live DC.\nRegistered.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real Active Directory discovery connector (go-ldap)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:24:23Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f7b8f3ef8b3ec02630763af4b91f0ce5b66ab881",
          "body": "Enumerate the projects the token is a member of (gitlab.com or a self-managed\ninstance via base_url); each project becomes a Repository asset, public projects\nraise a low-severity exposure finding. Paginated, context-aware. Registered.\nUnit-tested against an httptest GitLab API (real client, no network).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real GitLab discovery connector (gitlab-org/api)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:14:40Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b196c58eaca82f90b63e216510a0a52f46f506ea",
          "body": "Enumerate repositories the token can see — an organisation's repos when `org`\nis set, otherwise the authenticated user's — across github.com and GitHub\nEnterprise Server (`base_url`). Each repo becomes a Repository asset; publicly\nvisible non-archived repos raise a low-severity exposure finding. Paginated,\ncontext-aware. Registered in the scan registry. Unit-tested against an httptest\nGitHub API (real go-github client, no network).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scanner): real GitHub discovery connector (go-github)",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:08:28Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ed3e864c5b5328bc3d68adb3efc08172cd9560fd",
          "body": "Extend the discovery engine to cover every source category in spec \"6.\nDécouverte automatique des actifs\": Kubernetes, Docker, VMware, Active\nDirectory, Microsoft 365, GitHub, GitLab. Each is a SaaS-run provider that\nreuses the existing cloudScanner + CloudCollector seam + pipeline (encrypted\ncreds,\n[…]\ntions through to the runtime (was\n  nil) so API providers can read non-secret scoping options.\n- create_config: widen the invalid-provider message.\n\nCollectors are wired next, one provider per commit.",
          "is_bot": false,
          "headline": "feat(scanner): add 7 auto-discovery provider slots to the scan engine",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T10:00:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e87791b4259688cb2460ca79481ebe201649191b",
          "body": "…anagement\n\nTrack asset change history with user identification and email",
          "is_bot": false,
          "headline": "Merge pull request #135 from opendefender/feature/centralized-asset-m…",
          "author_name": "Alex DEMBELE",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T09:30:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "16368009ab5746e7f0bcd279b35fccaf8ca80f4b",
          "body": "…ment)\n\nROADMAP: mark the Asset Management row's history sub-function as fully tracing\nthe author; add an M3++ subsection. CLAUDE.md: sprint item #29 with the\ndiagnostic (inventory / criticality / dependency mapping already done; history\n\"who\" was the only real gap) and the live proof.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: record asset-history \"who\" completion (centralized asset manage…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T09:01:04Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5e4eef5f9e90aa792e62fa238566250f843379ce",
          "body": "Each history entry now shows the actor next to the timestamp: the resolved\nemail (changed_by_email) when available, a short actor id as fallback, and\n\"Système\"/\"System\" for unknown/legacy/system changes. New i18n keys\nassets.changedBy / assets.changedBySystem (FR + EN).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(frontend): show who changed an asset in its history drawer",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T08:37:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "80e102f7819518935e5bb96adccb968c830d7b85",
          "body": "…gen types\n\nDocument the asset history \"who\" on the contract and regenerate the frontend\ntypes (openapi-typescript). types/asset.ts already aliases the generated schema,\nso Asset history stays contract-first — no hand-written type. Additive only.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(openapi): add changed_by / changed_by_email to AssetSnapshot; re…",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T08:29:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7cfe1496200aa2156a6e19451a21fc32da55433a",
          "body": "- update/delete use-case tests assert the snapshot records ChangedBy.\n- new ListAssetSnapshots tests: emails resolved for distinct non-nil actors\n  (nil/system actor skipped, duplicate actor queried once); a failing lookup\n  degrades gracefully (no error, raw UUID preserved, empty email).\n- gorm ass\n[…]\nepo: the sqlite DDL for asset_snapshots had drifted (missing\n  changed_by); add the column and assert changed_by round-trips through the store.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(backend): cover asset history \"who\" tracking and email resolution",
          "author_name": "alex-dembele",
          "author_login": "alex-dembele",
          "committed_at": "2026-07-17T08:09:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        }
      ],
      "releases_count": 8,
      "commits_last_year": 776,
      "latest_release_at": "2026-07-17T08:13:10Z",
      "latest_release_tag": "1.0.8",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 26,
      "days_since_latest_release": 5,
      "mean_days_between_releases": 37.2
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 75,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/opendefender/openrisk",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/opendefender/openrisk",
          "is_deprecated": false,
          "latest_version": "v1.0.7",
          "repository_url": "https://github.com/opendefender/openrisk",
          "versions_count": 1,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-02-22T12:06:50Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 150
        }
      ]
    },
    "popularity": {
      "forks": 1,
      "stars": 14,
      "watchers": 3,
      "fork_history": {
        "days": [
          {
            "date": "2025-12-30",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 1,
        "total_forks": 1
      },
      "star_history": null,
      "open_issues_and_prs": 1
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": true,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "Makefile"
      ],
      "api_schema_files": [
        "docs/openapi.yaml"
      ],
      "has_devcontainer": false,
      "typecheck_configs": [
        "frontend/tsconfig.json"
      ],
      "toolchain_manifests": [
        "agent/go.mod",
        "backend/go.mod"
      ],
      "largest_source_bytes": 109967,
      "source_files_sampled": 825,
      "oversized_source_files": 2,
      "agent_instruction_files": [
        "CLAUDE.md",
        "docs/Claude.md"
      ],
      "agent_instruction_max_bytes": 130116
    },
    "dependencies": {
      "manifests": [
        "agent/go.mod",
        "backend/go.mod",
        "frontend/package.json"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "go",
        "npm"
      ],
      "dependencies": [
        {
          "name": "cloud.google.com/go/compute",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.64.0"
        },
        {
          "name": "github.com/Azure/azure-sdk-for-go/sdk/azcore",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.22.0"
        },
        {
          "name": "github.com/Azure/azure-sdk-for-go/sdk/azidentity",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.14.0"
        },
        {
          "name": "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.10.0"
        },
        {
          "name": "github.com/anthropics/anthropic-sdk-go",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.56.0"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.42.1"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2/config",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.32.30"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2/credentials",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.19.29"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2/service/ec2",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.316.1"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2/service/s3",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.105.1"
        },
        {
          "name": "github.com/aws/aws-sdk-go-v2/service/securityhub",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.73.1"
        },
        {
          "name": "github.com/docker/docker",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v28.5.2+incompatible"
        },
        {
          "name": "github.com/go-ldap/ldap/v3",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v3.4.14"
        },
        {
          "name": "github.com/go-pdf/fpdf",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.9.0"
        },
        {
          "name": "github.com/go-playground/validator/v10",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v10.28.0"
        },
        {
          "name": "github.com/gofiber/fiber/v2",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.52.12"
        },
        {
          "name": "github.com/golang-jwt/jwt/v5",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v5.3.1"
        },
        {
          "name": "github.com/golang-migrate/migrate/v4",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v4.19.1"
        },
        {
          "name": "github.com/google/go-github/v66",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v66.0.0"
        },
        {
          "name": "github.com/google/uuid",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.6.0"
        },
        {
          "name": "github.com/lib/pq",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.10.9"
        },
        {
          "name": "github.com/pquerna/otp",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.5.0"
        },
        {
          "name": "github.com/prometheus/client_golang",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.19.0"
        },
        {
          "name": "github.com/redis/go-redis/v9",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v9.6.3"
        },
        {
          "name": "github.com/rs/zerolog",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.35.0"
        },
        {
          "name": "github.com/skip2/go-qrcode",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.0.0-20200617195104-da1b6568686e"
        },
        {
          "name": "github.com/stretchr/testify",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.11.1"
        },
        {
          "name": "github.com/vmware/govmomi",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.55.1"
        },
        {
          "name": "gitlab.com/gitlab-org/api/client-go",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.46.0"
        },
        {
          "name": "golang.org/x/crypto",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.54.0"
        },
        {
          "name": "golang.org/x/oauth2",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.36.0"
        },
        {
          "name": "google.golang.org/api",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.288.0"
        },
        {
          "name": "gorm.io/datatypes",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.2.7"
        },
        {
          "name": "gorm.io/driver/postgres",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.6.0"
        },
        {
          "name": "gorm.io/driver/sqlite",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.6.0"
        },
        {
          "name": "gorm.io/gorm",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.31.1"
        },
        {
          "name": "k8s.io/api",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.34.2"
        },
        {
          "name": "k8s.io/apimachinery",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.34.2"
        },
        {
          "name": "k8s.io/client-go",
          "manifest": "backend/go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.34.2"
        },
        {
          "name": "@hello-pangea/dnd",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^18.0.1"
        },
        {
          "name": "@hookform/resolvers",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.2.2"
        },
        {
          "name": "@tanstack/react-query",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.0.0"
        },
        {
          "name": "axios",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^1.13.2"
        },
        {
          "name": "clsx",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^2.1.1"
        },
        {
          "name": "date-fns",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.1.0"
        },
        {
          "name": "framer-motion",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^12.23.24"
        },
        {
          "name": "leaflet",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^1.9.4"
        },
        {
          "name": "lodash",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.17.21"
        },
        {
          "name": "lucide-react",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^0.554.0"
        },
        {
          "name": "react",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^19.2.0"
        },
        {
          "name": "react-circular-progressbar",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^2.2.0"
        },
        {
          "name": "react-confetti",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^6.4.0"
        },
        {
          "name": "react-dom",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^19.2.0"
        },
        {
          "name": "react-grid-layout",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^1.5.2"
        },
        {
          "name": "react-hook-form",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^7.66.1"
        },
        {
          "name": "react-hot-toast",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^2.6.0"
        },
        {
          "name": "react-leaflet",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.0.0"
        },
        {
          "name": "react-router-dom",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^7.9.6"
        },
        {
          "name": "react-use",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^17.6.0"
        },
        {
          "name": "recharts",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^3.5.0"
        },
        {
          "name": "sonner",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^2.0.7"
        },
        {
          "name": "tailwind-merge",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^3.4.0"
        },
        {
          "name": "use-sound",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.0.0"
        },
        {
          "name": "zod",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.1.12"
        },
        {
          "name": "zustand",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.0.8"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 1,
        "merged_prs": 138,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 4,
        "closed_unmerged_prs": 2
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "alex-dembele",
          "commits": 772,
          "avatar_url": "https://avatars.githubusercontent.com/u/170009846?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "deploy.yml",
        "e2e.yml",
        "security-scanning.yml",
        "security.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        ".golangci.yml",
        "eslint.config.js"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "go.sum",
        "package-lock.json"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 9,
            "reason": "binaries present in source code",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": null,
            "reason": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 0,
            "reason": "0 out of 8 merged PRs checked by a CI test -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/8 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 3,
            "reason": "project has 1 contributing companies or organizations -- score normalized to 3",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 1,
            "reason": "dependency not pinned by hash detected -- score normalized to 1",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 10,
            "reason": "SAST tool is run on all commits",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 0,
            "reason": "73 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "6139cc36f3b4436b4e688af2621d4b5d7879b5e8",
        "ran_at": "2026-07-23T00:10:36Z",
        "aggregate_score": 4.8,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": true,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-22T19:08:28Z",
      "oldest_open_prs": [
        {
          "number": 148,
          "created_at": "2026-07-23T00:07:12Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-07-22T19:05:06Z",
      "ci_last_conclusion": "FAILURE",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/opendefender/OpenRisk",
    "host": "github.com",
    "name": "OpenRisk",
    "owner": "opendefender"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 63,
      "inputs": {
        "security": 48,
        "vitality": 89,
        "community": 50,
        "governance": 52,
        "engineering": 72
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 89,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "commits_last_year": 776,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 26
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "26/52 weeks with commits",
                "points": 18,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 26
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "776 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 776
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 8,
              "latest_release_tag": "1.0.8",
              "releases_from_tags": false,
              "days_since_latest_release": 5,
              "mean_days_between_releases": 37.2
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "8 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 8
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 5 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~37.2 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 37.2
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 0,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 0 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "moderate",
        "name": "Community & Adoption",
        "value": 50,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 20,
            "inputs": {
              "forks": 1,
              "stars": 14,
              "watchers": 3,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "14 stars",
                "points": 18.1,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 14
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "1 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "3 watchers",
                "points": 1.7,
                "status": "partial",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "excellent",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (AGPL-3.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "AGPL-3.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 52,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 13,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 1 contributing companies or organizations -- score normalized to 3",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 84,
            "inputs": {
              "merged_prs": 138,
              "open_issues": 0,
              "closed_issues": 4,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 2
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "100% of issues closed",
                "points": 46.8,
                "status": "met",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "138/140 decided PRs merged",
                "points": 37.7,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 138,
                      "decided": 140
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/8 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 41,
            "inputs": {
              "followers": 7,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "opendefender",
              "public_repos": 2,
              "account_age_days": 257
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "7 followers of opendefender",
                "points": 6.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 7,
                      "login": "opendefender"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "2 public repos, account ~0 yr old",
                "points": 4.9,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 2
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 0
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "good",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 84,
            "inputs": {
              "packages": [
                "github.com/opendefender/openrisk"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 150
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on go",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "go"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 150 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 150
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "1 published versions",
                "points": 4,
                "status": "partial",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 72,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "5 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": ".golangci.yml, eslint.config.js",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml, eslint.config.js"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "0 out of 8 merged PRs checked by a CI test -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "excellent",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "topics": [
                "analysis",
                "cybersecurity",
                "digital-forensics",
                "digital-forensics-analysis",
                "freesoftware",
                "incidence-response-plan",
                "observable",
                "obsevability",
                "python",
                "security-tools",
                "cyber-threat-intelligenece",
                "grc",
                "opendefender",
                "openrisk",
                "risk-analysis",
                "risk-assessment",
                "risk-management",
                "risks",
                "risks-score"
              ],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "19 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 19
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 48,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Branch-Protection, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "branch_protection",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 48,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 4.8
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "binaries present in source code",
                "points": 6.8,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "0 out of 8 merged PRs checked by a CI test -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/8 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 1 contributing companies or organizations -- score normalized to 3",
                "points": 0.8,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 1",
                "points": 0.5,
                "status": "partial",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is run on all commits",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "73 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "good",
        "name": "AI Readiness",
        "value": 80,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 1,
              "agent_instruction_files": [
                "CLAUDE.md",
                "docs/Claude.md"
              ],
              "agent_instruction_max_bytes": 130116
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md, docs/Claude.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md, docs/Claude.md"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "100 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 100,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 83,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum",
                "package-lock.json"
              ],
              "has_dockerfile": true,
              "typed_language": true,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "frontend/tsconfig.json"
              ],
              "agent_commit_share": 0.33,
              "toolchain_manifests": [
                "agent/go.mod",
                "backend/go.mod"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".golangci.yml, eslint.config.js",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml, eslint.config.js"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "frontend/tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "frontend/tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "Dockerfile, lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Dockerfile, lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "33 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 33,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 1",
                "points": 1,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "primary_language": "Go",
              "largest_source_bytes": 109967,
              "source_files_sampled": 825,
              "oversized_source_files": 2
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Go (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "2/825 source files over 60KB",
                "points": 54.9,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 825,
                      "oversized": 2
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [],
              "has_mcp_signal": false,
              "api_schema_files": [
                "docs/openapi.yaml"
              ]
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": "docs/openapi.yaml",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "docs/openapi.yaml"
                    }
                  }
                ],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "Could not fetch go package 'github.com/opendefender/openrisk-agent' from its registry",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-23T00:10:54.550218Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/o/opendefender/OpenRisk.svg",
  "full_name": "opendefender/OpenRisk",
  "license_state": "standard",
  "license_spdx": "AGPL-3.0"
}

Оцінки — це сигнали, а не гарантії. Вони відображають публічно видимі практики на GitHub — це не аудит коду й не гарантія безпеки.

Відсутні дані виключаються, а ваги перенормовуються — нуль за відсутність ніколи не ставиться. Методологія версіонована й відкрита: метрики v1.13.0, схема v0.27.0 — повна методологія · вікі метрик.

Як окремий результат виглядає на тлі всього реєстру: сукупна статистикаGo.