Публічний реєстр
Звіт про здоров'я програмного забезпеченнясхема 0.30.0 · метрики 2.5.0 · 2026-08-04 00:36 UTC

cert-pepper / cert-pepper

Adaptive CLI study tool for IT certification exams — FSRS spaced repetition meets AI explanations

PythonMIT★ 5 зірок⑂ 2 форкиз лют. 2026 р.Переглянути на GitHub ↗
ТипІнструмент командного рядкаMCP-серверяк це визначено

cert-pepper/cert-pepper має індекс здоров’я 65 зі 100, що відповідає смузі «Добрий». Найвищий показник — Vitality (79/100), найнижчий — Sustainability & Governance (37/100). Останнє оновлення — сьогодні. Більшість нещодавньої роботи виконує один учасник.

65
загалом / 100
Добрий

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

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

65
Винятковий93-100Верхній щабель реєстру (≈ топ-5%); відповідає практично всім перевіреним критеріям
Відмінний80-92Сильний за всіма напрямами; незначні прогалини
Добрий65-79Здоровий; прогалини обмежені та керовані
Помірний50-64Прийнятний, але з помітними прогалинами; рекомендовано перевірку
Слабкий35-49Суттєві недоліки в кількох сферах
У зоні ризику20-34Суттєві слабкі місця; впровадження потребує обережності
Критичний1-19Серйозні проблеми (покинутий, єдиний мейнтейнер, без базової гігієни)
ЖиттєздатністьСпільнота тавпровадженняСталість таврядуванняІнженернаякістьБезпекаГотовність доШІ

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

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

Зважений загальний бал 60 калібровано до 65 за шкалою опублікованого індексу (калібрування реєстру 2026-08-02).

Власність

cert-pepperОрганізація
0 підписників5 публічних репозиторіївз бер. 2026 р.

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

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

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

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

79Добрий · 21% загального індексу
Як обчислюється оцінка
36/36Свіжість push — останній push 0 дн. тому
6.9/36Ритм комітів — 10/52 тижнів із комітами
17.8/18Обсяг комітів — 94 комітів за останній рік
10/10OpenSSF Scorecard: Maintained — 13 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Використані вхідні дані
commits_last_year94
human_commit_share0,787
days_since_last_push0
active_weeks_last_year10
Як обчислюється оцінка
27/27Випускає релізи — опубліковано 8 релізів
27/36Свіжість релізів — останній реліз 140 дн. тому
27/27Ритм релізів — реліз кожні ~2,4 дн.
0/10OpenSSF Scorecard: Signed-Releases — немає даних
Використані вхідні дані
releases_count8
latest_release_tagv0.6.0
releases_from_tagsні
days_since_latest_release140
mean_days_between_releases2,4
Виключено з оцінювання (немає даних або не застосовно): OpenSSF Scorecard: Signed-Releases. Залишкові ваги перенормовано.

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

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

48Слабкий · 17% загального індексу
Як обчислюється оцінка
9.8/60Зірки — 5 зірок
0/25Форки — 2 форків
0/15Спостерігачі — 1 спостерігачів
Використані вхідні дані
forks2
stars5
watchers1
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Як обчислюється оцінка
22.5/22.5README
22.5/22.5Ліцензія — визнана ліцензія (MIT)
18/18Настанови CONTRIBUTING
13.5/13.5Кодекс поведінки
0/7.2Шаблон issue
6.3/6.3Шаблон PR
Використані вхідні дані
has_readmeтак
has_licenseтак
readme_badges3
has_contributingтак
has_issue_templateні
has_code_of_conductтак
readme_badge_servicesgithub.com, shields.io
has_pull_request_templateтак

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

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

37Слабкий · 23% загального індексу
Як обчислюється оцінка
9/54Бас-фактор — на 1 контриб’ютор(ів) припадає половина всіх комітів
3.2/22.5Розподіл комітів — головний контриб’ютор — автор 86% комітів
2.7/13.5Широта контриб’юторів — 2 контриб’юторів
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Використані вхідні дані
bus_factor1
contributors_sampled2
top_contributor_share0,857
Як обчислюється оцінка
42/42Вирішення issue — закрито 100% issue
14.7/30Прийняття PR — злито 23/47 вирішених PR
0/13Newcomer PR acceptance — за 30 дн. не вирішено жодного PR від новачка
0/15OpenSSF Scorecard: Code-Review — Found 1/14 approved changesets -- score normalized to 0
Використані вхідні дані
merged_prs23
open_issues0
closed_issues12
prs_merged_7d0
prs_decided_7d0
prs_merged_30d0
prs_decided_30d0
issue_closed_ratio1
closed_unmerged_prs24
first_time_authors_30d0
first_time_prs_merged_30d0
first_time_prs_decided_30d0
Виключено з оцінювання (немає даних або не застосовно): newcomer_pr_acceptance. Залишкові ваги перенормовано.
Як обчислюється оцінка
30/30Підтримка власника — у власності організації
0/20Верифікований домен
0/25Охоплення власника — 0 підписників у cert-pepper
6.4/25Послужний список — 5 публічних репозиторіїв, вік облікового запису ~0 р.
Використані вхідні дані
followers0
owner_typeOrganization
is_verified
owner_logincert-pepper
public_repos5
account_age_days140

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

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

71Добрий · 19% загального індексу
Як обчислюється оцінка
24/24Процеси CI — 3 процес(ів) CI
24/24Наявні тести
0/16Конфігурація лінтера
0/9.6Pre-commit-хуки
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 19 out of 19 merged PRs checked by a CI test -- score normalized to 10
Використані вхідні дані
has_ciтак
has_testsтак
has_editorconfigні
has_linter_configні
has_precommit_configні
Як обчислюється оцінка
30/30README
25/25Каталог документації
0/15Сайт документації / домашня сторінка
10/10Опис репозиторію
10/10Теми — 14 тем
0/10Wiki
Використані вхідні дані
topicsadaptive-learning, certification, claude, cli, education, exam-prep, flashcards, fsrs, mcp, python, security-plus, spaced-repetition, study, comptia
has_wikiні
homepage
has_readmeтак
has_docs_dirтак
has_descriptionтак

Безпека

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

63Помірний · 16% загального індексу

Стан безпеки

63Помірний
Як обчислюється оцінка
7.5/7.5Binary-Artifacts — no binaries found in the repo
6/7.5Branch-Protection — branch protection is not maximal on development and all release branches
2.5/2.5CI-Tests — 19 out of 19 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 1/14 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
7.5/7.5Dependency-Update-Tool — update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Ліцензія — license file detected
7.5/7.5Maintained — 13 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — немає даних
5/5Pinned-Dependencies — all dependencies are pinned
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 — 32 existing vulnerabilities detected
Використані вхідні дані
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate6,3
Виключено з оцінювання (немає даних або не застосовно): packaging, signed_releases. Залишкові ваги перенормовано.
Як обчислюється оцінка
9.1/35Прямі залежності без відомих сповіщень — уражено 4: mcp 1.26.0 (high 7.6), mistune 3.2.1 (high 7.5), pyjwt 2.12.1 (high 7.4), ще +1
0/25Непрямі залежності без відомих сповіщень — транзитивний набір не відокремлюється від залежностей розробки й тестування в цьому обсязі
40/40Немає задавнених сповіщень — жодне сповіщення не є публічним довше за 90 дн.
Використані вхідні дані
sourceosv
advisories57
affected_packages8
assessed_packages61
unassessed_packages3
affected_by_severityhigh 7, moderate 1
direct_affected_packages4
Виключено з оцінювання (немає даних або не застосовно): Непрямі залежності без відомих сповіщень. Залишкові ваги перенормовано. Звірено 61 резолвлених залежностей із OSV. 3 не вдалося оцінити — немає резолвленої версії, непідтримувана екосистема або поза межами звітованого переліку пакетів. Цей репозиторій не публікує пакета, який резолвить індекс, тож натомість оцінено граф залежностей репозиторію. Цей граф змішує закріплені версії для розробки й тестування зі справді постачаними залежностями, тож оцінюються лише задекларовані runtime-залежності; транзитивні знахідки подаються як контекст і в оцінку не входять. Досяжність не аналізується.

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

Наскільки репозиторій оснащений для розробки та супроводу за участі ШІ-агентів? Має свідомо малу вагу (4%): агентний інструментарій — реальний сигнал супроводу, але репозиторій без нього все одно може отримати 100/100.

75Добрий · 4% загального індексу
Як обчислюється оцінка
45/45Інструкції для агентів — AGENTS.md, CLAUDE.md
0/15Машиночитана документація (llms.txt)
40/40Читабельна історія комітів — намір зазначено у 69 з 74 людських комітів (структурований заголовок або пояснювальний текст)
Використані вхідні дані
has_llms_txtні
legible_history_share0,932
agent_instruction_filesAGENTS.md, CLAUDE.md
agent_instruction_max_bytes11 279
Як обчислюється оцінка
0/18Розгортання однією командою
22/22Автоматизовані тести
0/11Конфігурація лінтера / форматера
11/11Статична перевірка типів — cert_pepper/py.typed
10/10Відтворюване середовище — lockfile
10/10Підтверджена практика роботи з агентами — 61 з останніх 94 комітів створено агентом або з його зазначенням
8/8Автоматизоване супроводження — 20 з останніх 94 комітів — автоматичні оновлення залежностей
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Використані вхідні дані
has_nixні
has_testsтак
lockfilesuv.lock
has_dockerfileні
typed_languageні
bootstrap_files
has_devcontainerні
has_linter_configні
typecheck_configscert_pepper/py.typed
agent_commit_share0,649
toolchain_manifests
dependency_bot_commit_share0,213
Як обчислюється оцінка
27/45Типізований код — Python з конфігурацією перевірки типів (cert_pepper/py.typed)
55/55Керовані розміри файлів — 0/62 файлів вихідного коду понад 60 КБ
Використані вхідні дані
primary_languagePython
largest_source_bytes39 819
source_files_sampled62
oversized_source_files0
Як обчислюється оцінка
0/40Схема API (OpenAPI/GraphQL/proto)
20/20Сервер MCP
40/40Придатні до запуску приклади — examples
Використані вхідні дані
example_dirsexamples
has_mcp_signalтак
api_schema_files

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

5зірок GitHub
2контриб'юторів
94комітів за останні 12 місяців
0днів від останнього пушу
8релізів
1бас-фактор
0відкритих issue
PyPIпакетних екосистем

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

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • Could not fetch pypi package 'cert-pepper' from its registry

Докладніше

Історія зірок і форків 0 ★ / 2 ⇿
0Зірки
2Форки

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

111222212026-032026-042026-04
OpenSSF Scorecard 6.3 / 10
6.3сукупно

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

10Binary-Artifactsno binaries found in the repo
8Branch-Protectionbranch protection is not maximal on development and all release branches
10CI-Tests19 out of 19 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 1/14 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained13 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
н/дPackagingpackaging workflow not detected
10Pinned-Dependenciesall dependencies are pinned
10SASTSAST tool is run on all commits
10Security-Policysecurity policy file detected
н/дSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities32 existing vulnerabilities detected
Прямі залежності 13
РеєстрПакетОбмеження версіїМаніфест
PyPItyper>=0.12.0pyproject.toml
PyPIrich>=13.7.0pyproject.toml
PyPIpydantic>=2.6.0pyproject.toml
PyPIpydantic-settings>=2.2.0pyproject.toml
PyPIsqlalchemy>=2.0.0pyproject.toml
PyPIaiosqlite>=0.20.0pyproject.toml
PyPIanthropic>=0.34.0pyproject.toml
PyPImcp>=1.0.0pyproject.toml
PyPImistune>=3.2.1pyproject.toml
PyPIpython-dotenv>=1.2.2pyproject.toml
PyPIpyyaml>=6.0.0pyproject.toml
PyPIhttpx>=0.27.0pyproject.toml
PyPIpyjwt>=2.12.0pyproject.toml
Усі залежності 64

Повний розв'язаний набір залежностей із графа залежностей GitHub: 14 прямих і 50 непрямих (транзитивних) пакетів. Транзитивне замикання є повним, коли в репозиторії закомічено lockfile.

РеєстрПакетВерсіяЗв'язок
PyPIaiosqlite0.22.1пряма
PyPIanthropic0.84.0пряма
PyPIhttpx0.28.1пряма
PyPImcpпряма
PyPImcp1.26.0пряма
PyPImistune3.2.1пряма
PyPIpydantic2.12.5пряма
PyPIpydantic-settings2.13.1пряма
PyPIpyjwt2.12.1пряма
PyPIpython-dotenv1.2.2пряма
PyPIpyyaml6.0.3пряма
PyPIrich14.3.3пряма
PyPIsqlalchemy2.0.47пряма
PyPItyper0.24.1пряма
PyPIannotated-doc0.0.4непряма
PyPIannotated-types0.7.0непряма
PyPIanyio4.12.1непряма
PyPIattrs25.4.0непряма
PyPIcertifi2026.2.25непряма
PyPIcffi2.0.0непряма
PyPIclick8.3.1непряма
PyPIcolorama0.4.6непряма
PyPIcoverage7.13.4непряма
PyPIcryptography46.0.7непряма
PyPIdistro1.9.0непряма
PyPIdocstring-parser0.17.0непряма
PyPIgreenlet3.3.2непряма
PyPIh110.16.0непряма
PyPIhatch-vcsнепряма
PyPIhatchlingнепряма
PyPIhttpcore1.0.9непряма
PyPIhttpx-sse0.4.3непряма
PyPIidna3.15непряма
PyPIiniconfig2.3.0непряма
PyPIjiter0.13.0непряма
PyPIjsonschema4.26.0непряма
PyPIjsonschema-specifications2025.9.1непряма
PyPIlibrt0.8.1непряма
PyPImarkdown-it-py4.0.0непряма
PyPImdurl0.1.2непряма
PyPImypy1.19.1непряма
PyPImypy-extensions1.1.0непряма
PyPIpackaging26.0непряма
PyPIpathspec1.0.4непряма
PyPIpluggy1.6.0непряма
PyPIpycparser3.0непряма
PyPIpydantic-core2.41.5непряма
PyPIpygments2.20.0непряма
PyPIpytest9.0.3непряма
PyPIpytest-asyncio1.3.0непряма
PyPIpytest-cov7.0.0непряма
PyPIpython-multipart0.0.27непряма
PyPIpywin32311непряма
PyPIreferencing0.37.0непряма
PyPIrpds-py0.30.0непряма
PyPIruff0.15.4непряма
PyPIshellingham1.5.4непряма
PyPIsniffio1.3.1непряма
PyPIsse-starlette3.2.0непряма
PyPIstarlette1.0.1непряма
PyPItypes-pyyaml6.0.12.20250915непряма
PyPItyping-extensions4.15.0непряма
PyPItyping-inspection0.4.2непряма
PyPIuvicorn0.41.0непряма
Сповіщення про залежності 8

Цей репозиторій не публікує пакета, який розпізнає індекс, тож оцінено його власний граф залежностей — 61 пакетів, серед яких є й піниї розробки та тестування, що ніколи не постачаються: 8 мають відомі сповіщення, з них 4 прямі. 3 не вдалося оцінити — немає резолвленої версії, непідтримувана екосистема або поза наведеним переліком пакетів.

ПакетВерсіяЗв'язокКритичністьСповіщеньВиправлено в
mcp1.26.0прямависока61.28.1
mistune3.2.1прямависока203.3.0
pyjwt2.12.1прямависока92.13.0
click8.3.1непрямависока18.3.3
cryptography46.0.7непрямависока450.0.0
python-multipart0.0.27непрямависока80.0.31
starlette1.0.1непрямависока81.3.1
pydantic-settings2.13.1прямапомірна12.14.2

Сповіщення означає, що версія, записана в графі залежностей, потрапляє в уражений діапазон. Досяжність не аналізується, а граф містить піниї розробки й тестування — знахідка може стосуватися інструментів, а не поставленого коду.

Звіт у форматі JSON машиночитний
{
  "data": {
    "repo": {
      "topics": [
        "adaptive-learning",
        "certification",
        "claude",
        "cli",
        "education",
        "exam-prep",
        "flashcards",
        "fsrs",
        "mcp",
        "python",
        "security-plus",
        "spaced-repetition",
        "study",
        "comptia"
      ],
      "is_fork": false,
      "size_kb": 2434,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Shell": 1239,
        "Python": 464254
      },
      "pushed_at": "2026-08-03T22:48:46Z",
      "created_at": "2026-02-27T19:30:05Z",
      "owner_type": "Organization",
      "updated_at": "2026-06-04T18:25:30Z",
      "description": "Adaptive CLI study tool for IT certification exams — FSRS spaced repetition meets AI explanations",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": null,
      "name": null,
      "type": "Organization",
      "login": "cert-pepper",
      "company": null,
      "location": null,
      "followers": 0,
      "avatar_url": "https://avatars.githubusercontent.com/u/268744883?v=4",
      "created_at": "2026-03-16T22:12:52Z",
      "is_verified": null,
      "public_repos": 5,
      "account_age_days": 140
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.6.0",
          "kind": "minor",
          "published_at": "2026-03-16T23:21:24Z"
        },
        {
          "tag": "v0.5.4",
          "kind": "patch",
          "published_at": "2026-03-10T20:41:11Z"
        },
        {
          "tag": "v0.5.3",
          "kind": "patch",
          "published_at": "2026-03-09T20:15:57Z"
        },
        {
          "tag": "v0.5.2",
          "kind": "patch",
          "published_at": "2026-03-09T19:04:03Z"
        },
        {
          "tag": "v0.5.1",
          "kind": "patch",
          "published_at": "2026-03-04T21:56:34Z"
        },
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2026-03-04T15:11:41Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-03-02T14:52:36Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-02-28T03:20:53Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "1a9360050a4727fc34bccbbce0ff96b71005bcb7",
          "body": "…1 directory (#44)\n\nBumps the uv group with 1 update in the / directory:\n[starlette](https://github.com/Kludex/starlette).\n\nUpdates `starlette` from 0.52.1 to 1.0.1\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/Kludex/starlette/releases\">starlette's\nrelea\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump starlette from 0.52.1 to 1.0.1 in the uv group across …",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-04T18:25:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7e9be09fbb89203e13c4ff9c890ee2297221f80c",
          "body": "…ory (#39)\n\nBumps the uv group with 1 update in the / directory:\n[idna](https://github.com/kjd/idna).\n\nUpdates `idna` from 3.11 to 3.15\n<details>\n<summary>Changelog</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/kjd/idna/blob/master/HISTORY.md\">idna's\nchangelog</a>.</em></p>\n<blockquote>\n\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump idna from 3.11 to 3.15 in the uv group across 1 direct…",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-05-29T14:12:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "19f542e9f0c1bd1ccb7c426b72aac285b13be0f5",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 4.35.3 to 4.36.0.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.36.0</h2>\n<ul>\n<li>\n[…]\npendabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: crook3dfingers <58201909+crook3dfingers@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 4.35.3 to 4.36.0 (#38)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-05-29T14:09:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c999e721097c588185c677c3c673362bee577762",
          "body": "… across 1 directory (#35)\n\nBumps the uv group with 1 update in the / directory:\n[python-multipart](https://github.com/Kludex/python-multipart).\n\nUpdates `python-multipart` from 0.0.26 to 0.0.27\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/Kludex/python-\n[…]\npendabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: crook3dfingers <58201909+crook3dfingers@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump python-multipart from 0.0.26 to 0.0.27 in the uv group…",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-05-29T14:08:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f5a43e3b9944c70bb6b2063e1c21e3598b64d2a3",
          "body": null,
          "is_bot": false,
          "headline": "Update migration test for max score migration",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-29T14:05:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1e21aa2b22042be67c93199ff575897775f5662",
          "body": null,
          "is_bot": false,
          "headline": "Fix mypy Any return errors",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-29T14:02:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2805adc5d681b77c47a84c8cf01cdaefa1a8e6ef",
          "body": null,
          "is_bot": false,
          "headline": "Fix study CLI import lint",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-29T13:59:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c0cfe1ff208fdccdc751e83b714035c63a3f1705",
          "body": null,
          "is_bot": false,
          "headline": "feat: add multi-exam study selection",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-11T01:34:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "779c0e229227f758ade996884eba0bc6efcebc1e",
          "body": null,
          "is_bot": false,
          "headline": "Restore non-MCP exam generation fallback",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-10T22:32:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e43337333792d80dd736d9608264f34739c33f39",
          "body": null,
          "is_bot": false,
          "headline": "Improve MCP question-bank flow and session recovery",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-10T21:52:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "265d84865a39fff1db7b3368bfe0c93b9eb3b03c",
          "body": null,
          "is_bot": false,
          "headline": "feat: add sized exam generation tiers",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-05-10T21:31:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "061a101e1e242038fee4ce7c99b1e00d4dc97f01",
          "body": "Bumps the uv group with 2 updates in the / directory:\n[mistune](https://github.com/lepture/mistune) and\n[python-dotenv](https://github.com/theskumar/python-dotenv).\n\nUpdates `mistune` from 3.2.0 to 3.2.1\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/leptu\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump the uv group across 1 directory with 2 updates (#34)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-05-10T20:53:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b95cea43d4504010d890e0f77533a80d86d2de81",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 4.35.2 to 4.35.3.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.35.3</h2>\n<ul>\n<li>\n[…]\nnless you reopen the\nPR or upgrade to it yourself)\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 4.35.2 to 4.35.3 (#33)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-05-10T20:53:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a27bfa6b37d3351a0efbcd404f9be15093a12cc4",
          "body": "… across 1 directory (#29)\n\nBumps the uv group with 1 update in the / directory:\n[python-multipart](https://github.com/Kludex/python-multipart).\n\nUpdates `python-multipart` from 0.0.22 to 0.0.26\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/Kludex/python-\n[…]\n/0.0.22...0.0.26\">compare\nview</a></li>\n</ul>\n</details>\n<br />\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump python-multipart from 0.0.22 to 0.0.26 in the uv group…",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-30T14:56:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "007b5d12ebf7b8ff93dbd64bf0f188d26d72a6fa",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 4.35.1 to 4.35.2.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.35.2</h2>\n<ul>\n<li>\n[…]\nd5c52f41a045d225\">compare\nview</a></li>\n</ul>\n</details>\n<br />\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 4.35.1 to 4.35.2 (#30)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-30T14:53:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8a93d406969cae66828ceee6b9a2eddf8a254559",
          "body": "Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from\n8.0.0 to 8.1.0.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/astral-sh/setup-uv/releases\">astral-sh/setup-uv's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v8.1.0 🌈 New input <code>no-proj\n[…]\nd8ec9567f424441b\">compare\nview</a></li>\n</ul>\n</details>\n<br />\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump astral-sh/setup-uv from 8.0.0 to 8.1.0 (#31)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-30T14:50:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "46c6d371c5112f9213ad4ba9f7e53a26e160d0f8",
          "body": "Adds CY0-001 (SecAI+) as a second supported exam alongside Security+ and a new flashcard review command.\n\n**SecAI+ exam content**\n- 118 practice questions (D1: 20, D2: 48, D3: 27, D4: 23 — distribution matches the 17/40/24/19 weights)\n- 135 flashcards in `examples/secai-plus/flashcards/key-concepts.\n[…]\nplaced paid `gitleaks-action` with the free gitleaks CLI binary (org repos can't use the action without a license)\n- `.claude/settings.json` hooks now use working-directory-relative paths\n\nCloses #32.",
          "is_bot": false,
          "headline": "feat: add SecAI+ exam support and flashcard CLI command (#32)",
          "author_name": "Security Chops",
          "author_login": "securitychops",
          "committed_at": "2026-04-30T14:45:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "496d139f8de47d57b4070464dd985273fb5c700a",
          "body": "…ss 1 directory (#27)\n\nBumps the uv group with 1 update in the / directory:\n[pytest](https://github.com/pytest-dev/pytest).\n\nUpdates `pytest` from 9.0.2 to 9.0.3\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/pytest-dev/pytest/releases\">pytest's\nreleases</\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore(deps-dev): bump pytest from 9.0.2 to 9.0.3 in the uv group acro…",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-15T18:08:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b76882a77ca7eb683de573e1745f95f71ee56b83",
          "body": "…across 1 directory (#26)\n\nBumps the uv group with 1 update in the / directory:\n[cryptography](https://github.com/pyca/cryptography).\n\nUpdates `cryptography` from 46.0.6 to 46.0.7\n<details>\n<summary>Changelog</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/pyca/cryptography/blob/main/CHANG\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump cryptography from 46.0.6 to 46.0.7 in the uv group …",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-15T18:05:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e39247aa111d5993661cce6af502a4c3d5f4e69a",
          "body": "Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from\n7.6.0 to 8.0.0.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/astral-sh/setup-uv/releases\">astral-sh/setup-uv's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v8.0.0 🌈 Immutable releases and \n[…]\nnless you reopen the\nPR or upgrade to it yourself)\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump astral-sh/setup-uv from 7.6.0 to 8.0.0 (#25)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-15T18:05:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3e9478dcd59b9e1b1d54b718f8f03036b7b30ad4",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 4.34.1 to 4.35.1.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.35.1</h2>\n<ul>\n<li>\n[…]\nnless you reopen the\nPR or upgrade to it yourself)\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 4.34.1 to 4.35.1 (#24)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-04-15T18:05:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8a986bd6cf9904e68281ff80435fc0d3d59c1f7d",
          "body": "ReDoS vulnerability in GUID regex matching (low severity).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(deps): bump Pygments to 2.20.0 to resolve CVE-2026-4539",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-30T17:00:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "050145496844ff24af47ccbcad6c36beee38a233",
          "body": "The GITLEAKS_LICENSE secret is not available to Dependabot, causing\nthe scan job to fail on every dependency bump PR. Skip the scan steps\n(not the job) so the status check still reports success.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: skip gitleaks scan for Dependabot PRs",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-30T16:58:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6ce1ce5ba708bdbc88b3b0c66ec084b0a605df08",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 4.33.0 to 4.34.1.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.34.1</h2>\n<ul>\n<li>\n[…]\nnless you reopen the\nPR or upgrade to it yourself)\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 4.33.0 to 4.34.1 (#22)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-30T16:57:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3641aa481cdfdd61a118304d85dd90f7efcfc96f",
          "body": "…across 1 directory (#23)\n\nBumps the uv group with 1 update in the / directory:\n[cryptography](https://github.com/pyca/cryptography).\n\nUpdates `cryptography` from 46.0.5 to 46.0.6\n<details>\n<summary>Changelog</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/pyca/cryptography/blob/main/CHANG\n[…]\ngithub.com/cert-pepper/cert-pepper/network/alerts).\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump cryptography from 46.0.5 to 46.0.6 in the uv group …",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-30T16:56:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "585dd97c26520525930d836de65adc791faa03f7",
          "body": "## Summary\n- Adds a Stop hook script that blocks Claude from ending a turn after\ninvoking doc-editor without following up with an Edit or Write\n- Updates doc-editor SKILL.md description to emphasize it is a\npreprocessing step\n- Adds ACTION REQUIRED footer to SKILL.md output\n- Documents the mandatory\n[…]\n temp files\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Developer <dev@crook3d.com>\nCo-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: add stop hook for doc-editor workflow (#21)",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-18T16:48:22Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e209b7b926c1944699d22e2c26cbd0cb3d709237",
          "body": "## Summary\n- **README overhaul** — restructured for impact-first ordering:\npositioning tagline, terminal demo GIF, quick start, \"Why CertPepper?\"\ndifferentiators vs Anki/Quizlet, consolidated feature table\n- **Terminal demo** — VHS tape + 22-second GIF showing `study` and\n`progress` commands\n- **`py\n[…]\n Review demo GIF quality\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Developer <dev@crook3d.com>\nCo-authored-by: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: OSS adoption improvements (#20)",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-17T04:08:25Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ce90fbb4335ee1ea68a904f61c6e8a9cb04ef6ab",
          "body": "PyJWT <2.12.0 accepts unknown `crit` header extensions. It's a transitive\ndependency via mcp — adding an explicit lower bound forces the patched version.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(deps): pin PyJWT>=2.12.0 to resolve CVE (GHSA high)",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-17T02:42:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bece3444a51947f2ac551285ba19cb8e4f01f8d8",
          "body": "Bumps [github/codeql-action](https://github.com/github/codeql-action)\nfrom 3.33.0 to 4.33.0.\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github.com/github/codeql-action/releases\">github/codeql-action's\nreleases</a>.</em></p>\n<blockquote>\n<h2>v4.33.0</h2>\n<ul>\n<li>\n[…]\nnless you reopen the\nPR or upgrade to it yourself)\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump github/codeql-action from 3.33.0 to 4.33.0 (#19)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-17T02:41:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9783fe01aa4aabf66d7776ba7bcbb178ec4c7256",
          "body": "…nning\n\nAdd CodeQL code scanning workflow for Python (push, PR, weekly schedule).\nUpdate dependabot.yml with commit message prefixes, reviewer assignment,\nand PR limits. Enable GitHub secret scanning + push protection via API.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: add CodeQL scanning, enhance Dependabot config, enable secret sca…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-17T02:34:27Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9ef44adf0cea3b358e3f8f17903707186dbf1c3a",
          "body": "Add CODEOWNERS file assigning all paths to @crook3dfingers. Update\nCONTRIBUTING.md with fork-and-branch workflow, branch naming prefixes,\nsquash-merge policy, and review re-approval requirement.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "repo: add CODEOWNERS, update CONTRIBUTING for new repo rules",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T23:19:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "88c8657cb936f9a01b55597d4ea1326ed7fc7ef1",
          "body": "The `version` input still works as an alias but `uv-version` is the\ncanonical name since setup-uv v5.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: rename deprecated `version` input to `uv-version`",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T22:35:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cc342c3d06ee543cac2a9ac860233f920e85f837",
          "body": "…ral-sh/setup-uv-7.6.0\n\nchore(deps): bump astral-sh/setup-uv from 4.2.0 to 7.6.0",
          "is_bot": false,
          "headline": "Merge pull request #18 from cert-pepper/dependabot/github_actions/ast…",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-16T22:34:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da9087192b8507d0a690228b3d8f94f5eff20b7b",
          "body": "Organization repos require a license key for gitleaks. The secret is\nset on the repo; this wires it into the workflow environment.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: pass GITLEAKS_LICENSE secret to gitleaks action",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T22:29:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f27ec97ddb34b6992bbce1cb0cd18caf02964d75",
          "body": "Transfer repo from crook3dfingers to cert-pepper org and update all\nreferences. Add CHANGELOG entry for v0.6.0 covering: parameterized SQL\nin pregenerate, tagline positioning, doc accuracy fixes (session size,\nacronym counts, walkthrough version), new domain 3/5 practice questions,\nand Dependabot dependency bumps.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: v0.6.0 — org transfer, SQL fix, content updates",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T22:18:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fb3f1ff0b888608a1ff1cfe1745afd24ed4a9bb6",
          "body": "Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4.2.0 to 7.6.0.\n- [Release notes](https://github.com/astral-sh/setup-uv/releases)\n- [Commits](https://github.com/astral-sh/setup-uv/compare/38f3f104447c67c051c4a08e39b64a148898af3a...37802adc94f370d6bfd71619e3f0bf239e1f3b78)\n\n---\n[…]\ndependency-name: astral-sh/setup-uv\n  dependency-version: 7.6.0\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump astral-sh/setup-uv from 4.2.0 to 7.6.0",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-16T22:14:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4fa5d949b8165d8b757e86adfbfb780a4ac973aa",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: make tagline all caps in README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T21:54:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1246dcf02de8772e61121fa894d0e006ae7a9c79",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: move tagline inside centered div below logo",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T21:54:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2f6988de1004e8ebf0699a0c3bca7e422b1a95ee",
          "body": "…actions/checkout-6.0.2\n\nchore(deps): bump actions/checkout from 4.3.1 to 6.0.2",
          "is_bot": false,
          "headline": "Merge pull request #16 from crook3dfingers/dependabot/github_actions/…",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-16T21:32:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18fdacce5c94368a5c625e01036509236cc7f91a",
          "body": "…astral-sh/setup-uv-38f3f104447c67c051c4a08e39b64a148898af3a\n\nchore(deps): bump astral-sh/setup-uv from e4db8464a088ece1b920f60402e813ea4de65b8f to 38f3f104447c67c051c4a08e39b64a148898af3a",
          "is_bot": false,
          "headline": "Merge pull request #15 from crook3dfingers/dependabot/github_actions/…",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-16T21:32:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e346efd3514d581a1ed4ee5408cc501d6def2699",
          "body": "Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from e4db8464a088ece1b920f60402e813ea4de65b8f to 38f3f104447c67c051c4a08e39b64a148898af3a.\n- [Release notes](https://github.com/astral-sh/setup-uv/releases)\n- [Commits](https://github.com/astral-sh/setup-uv/compare/e4db8464a088ece1b92\n[…]\ncies:\n- dependency-name: astral-sh/setup-uv\n  dependency-version: 38f3f104447c67c051c4a08e39b64a148898af3a\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump astral-sh/setup-uv",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-16T21:31:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dc8789642c737b038bc0a7091bf5b4e7aaa01b68",
          "body": "Bumps [actions/checkout](https://github.com/actions/checkout) from 4.3.1 to 6.0.2.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/34e114876b0b11c390a5638\n[…]\n- dependency-name: actions/checkout\n  dependency-version: 6.0.2\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump actions/checkout from 4.3.1 to 6.0.2",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-16T21:31:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f7a690580032f6f4ddd1aa025ff0ee39b438b2f7",
          "body": "…gitleaks/gitleaks-action-ff98106e4c7b2bc287b24eaf42907196329070c7\n\nchore(deps): bump gitleaks/gitleaks-action from dcedce43c6f43de0b836d1fe38946645c9c638dc to ff98106e4c7b2bc287b24eaf42907196329070c7",
          "is_bot": false,
          "headline": "Merge pull request #17 from crook3dfingers/dependabot/github_actions/…",
          "author_name": "crook3dfingers",
          "author_login": "crook3dfingers",
          "committed_at": "2026-03-16T21:30:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d064840d9436991a31713a55eaf6e86c34a45b14",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add nav links and value proposition to README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T21:15:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a5861ea84caaecd6ee20ce6ad93172610bab6b62",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: make tagline bold italic in README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:51:35Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "84c9a07028d2936962f6773d80e31ac90aac2cb8",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: bold tagline and add spacing below badges in README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:47:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "421d13ac3233d520e6bb59066d976f3dd1299add",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: restore tagline and remove SQLite from README intro",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:42:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0e05dc0dfbd4cf756e9e3392d1909b12a132db7f",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: replace logo with tighter crop",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:36:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a798ab04099cd58fb889254b550759b48391409c",
          "body": "Collapse separate <p> tags into a single <div> to eliminate\nGitHub's default paragraph margins between logo and badges.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: reduce spacing around logo in README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:26:31Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "73e1f28cc7bc7454b13625853fcca7e21687591f",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add heading with tagline, enlarge logo in README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:20:26Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fa64cbf19a2fb9ad74e3feaa64fa732ec4c4612d",
          "body": "Rename all user-facing \"cert-pepper\" references to \"CertPepper\" across\ndocs, CLI headers, MCP reports, and metadata. Add centered logo to\nREADME. Package name, CLI command, URLs, and MCP server names unchanged.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: rename display name to CertPepper and add logo to README",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T20:12:30Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "215e1c5fa36f5179b8c782459dc0df7aacc86125",
          "body": "Bumps [gitleaks/gitleaks-action](https://github.com/gitleaks/gitleaks-action) from dcedce43c6f43de0b836d1fe38946645c9c638dc to ff98106e4c7b2bc287b24eaf42907196329070c7.\n- [Release notes](https://github.com/gitleaks/gitleaks-action/releases)\n- [Commits](https://github.com/gitleaks/gitleaks-action/com\n[…]\n- dependency-name: gitleaks/gitleaks-action\n  dependency-version: ff98106e4c7b2bc287b24eaf42907196329070c7\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "chore(deps): bump gitleaks/gitleaks-action",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-03-16T19:17:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6a6fc5f219d2b2de24cd771def614cda343d5036",
          "body": "Closes #11, closes #12, closes #13\n\n- Pin actions/checkout and astral-sh/setup-uv to commit SHAs\n- Add gitleaks workflow for secret scanning on push/PR\n- Add dependabot for pip and github-actions (weekly)\n- Add Changelog and Issues URLs to pyproject.toml\n- Repo description and topics set via gh repo edit\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: harden CI, add dependabot, and update pyproject URLs",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T19:16:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d971e73170b89959fedc27c63ccd741a6a3869c2",
          "body": "Closes #5, closes #6, closes #7, closes #8, closes #9\n\n- CONTRIBUTING.md: dev setup, TDD workflow, PR checklist\n- CODE_OF_CONDUCT.md: Contributor Covenant v2.1 reference\n- SECURITY.md: GitHub private vulnerability reporting, 90-day disclosure\n- CHANGELOG.md: full history from v0.1.0 through v0.5.4\n-\n[…]\n(bug report, feature request, template chooser)\n- Pull request template with TDD checklist\n- Gitleaks scan passed clean (no secrets in history)\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add community health files, CHANGELOG, and GitHub templates",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T19:16:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ed6e011021c4356f69279138b80a826dba3191f6",
          "body": "Emoji characters (🌶️, 🫑, 🔥) render as broken boxes in terminals\nlacking glyph coverage (e.g. screen-256color/tmux). Replace with a\n10-char heat bar using █/░ block-drawing characters that render in\nany monospace font, colored green/yellow/red via Rich markup.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: replace emoji with ASCII heat bar in Pepper Score display",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-16T18:28:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5700c97b05c2d1463f6ce08a14218addf67ee264",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: break long line in exam.py to satisfy ruff E501",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-13T15:08:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fc2fabca0848a4495e99cbde3431de0a0d78fb88",
          "body": "`while True` never triggers the `else` clause (Python only runs `while/else`\nwhen the loop condition becomes False), so `answer` was left unbound after a\nKeyboardInterrupt. Replace the broken `while/else` with a `quit_session` flag.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: resolve UnboundLocalError when user quits mid-question",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-13T15:08:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5b6ba739e203180750bfbaf06df0c95acbe70477",
          "body": "- Add _timer_thread that rewrites the timer line in-place every second\n  using ANSI cursor-control codes; timer appears above the question header\n- Use SIGALRM to immediately interrupt Prompt.ask() when time expires,\n  ending the exam without requiring an extra Enter press\n- format_time and _timer_thread covered by tests/test_exam_cli.py\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: live countdown timer in exam mode with SIGALRM expiry",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-10T20:41:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "111224abf82545bef9bbe1c08fc6aa28b13e4548",
          "body": "select_exam_questions() now uses a two-pass approach per domain:\npass 1 picks unseen questions (no prior question_attempts); pass 2\ntops up with seen questions only when the unseen pool runs short.\n\nThe exam start screen now shows \"N questions selected — X new, Y\npreviously seen\" so users know how m\n[…]\ng attempt history on re-ingest\n- CLAUDE.md/README.md/docs/walkthrough.md: doc updates for\n  coverage-adjusted scorer and --new-questions flag\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: exam mode prefers unseen questions with freshness summary",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-10T20:15:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "96e395818ad9f31750cac702395927f9c980472a",
          "body": "…ched\n\nDomain status now uses domain_status_label() which requires ≥50% of a\ndomain's questions to have been attempted before showing \"Mastered\".\nBelow that threshold, high accuracy shows \"On Track\" instead — consistent\nwith how exam readiness says \"TOO EARLY TO TELL\" before 50% overall\ncoverage. Adds tests/test_progress.py with 9 tests for the helper.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: suppress \"Mastered\" in domain performance until 50% coverage rea…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-09T20:52:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8f1c71d951d278d3284c1b82bb7961e0b0497d9f",
          "body": "Previously fell back to Tier 4 (least-recently-attempted), which would\nre-serve already-seen questions against the caller's intent. Now returns\nNone so the CLI can cleanly end the session with an appropriate message.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: new_only mode returns None when no unseen questions remain",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-09T20:15:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5b1c5141ccad3df7a26de38f332609844f61845c",
          "body": "…estions\n\nPredicted score now blends observed accuracy with a 50% uninformed prior\nfor questions not yet seen, making the score conservative at low coverage\nand convergent at full coverage. Gates \"Exam Readiness: READY\" on ≥50%\nquestion bank coverage to prevent misleading READY status at low coverag\n[…]\npredict_score tool\n- Add Score Confidence section to MCP progress_dashboard resource\n- Add TestCoverageAdjustedScore (5 tests, TDD red-green)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: coverage-adjusted score prediction with 50% prior for unseen qu…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-09T20:03:07Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2d9dda722bf9258299d4666c118314b49a7a4726",
          "body": "Break long lines 105 and 111 to comply with 100-character limit.\n- Line 105: split new_questions parameter definition\n- Line 111: split asyncio.run() call across multiple lines\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: resolve ruff line-too-long violations in main.py",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-09T19:04:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d261e04fd62afc63b36772ca44df1b8fa3485ab1",
          "body": "Adds new_only parameter to select_question() that skips Tiers 1–2\n(due review/learning cards) and goes straight to unseen questions.\nExposed as --new-questions CLI flag and new_only MCP session param.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add --new-questions flag to study command",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-09T19:03:14Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7b3291b74693b4ed062d6f00ddea1314f5026e62",
          "body": "Add a rule to the _QUESTIONS_SYSTEM prompt so setup_exam() never spells\nout acronym meanings in answer options. Candidates must recognise bare\nacronyms — writing them out removes that test point entirely.\n\nAlso documents the rule in CLAUDE.md for manual question authoring.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: enforce no-acronym-expansion rule in generated questions",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T21:56:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1a3b1d459d292b0e58da0a28449c866f0c64cecd",
          "body": "…fetching\n\n- ci-watcher.md: SHA now obtained via git rev-parse HEAD internally\n- ci-watcher.md: on failure, fetches and tails workflow run logs via gh run view --log-failed\n- CLAUDE.md: update Skills section — no SHA substitution needed by caller\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: update ci-watcher skill with self-obtained SHA and failure log …",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T17:01:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "670e1c66b83e698fac425ffb849731b9f79d4f66",
          "body": "A wrong SHA caused a silent 422 loop for 20 minutes. Now stderr is\ncaptured to a temp file and triggers an immediate exit 1 with the\nerror printed. Added a note that the full SHA must come from\ngit rev-parse HEAD, never abbreviated or guessed.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: surface API errors in ci-watcher instead of swallowing them",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:54:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1a7215984417d4e57b1fa290715674b350eabbb6",
          "body": "- Add GoalRow TypedDict so get_goal's return is fully typed\n- Rename loop variable cal_row to avoid shadowing the DB row on line 116\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: resolve mypy type errors in goals.py and goal.py",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:41:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "617ba9b6c543ab427bde1b91ab04aed4a3c25434",
          "body": "Explain how to invoke skills as background Haiku Agent calls so the\nci-watcher is actually launched after every git push.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add skill invocation instructions to CLAUDE.md Skills section",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:38:22Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c0f3534cc7c0d7a8509f71fb19ce0cb5ad4fd41c",
          "body": null,
          "is_bot": false,
          "headline": "fix: release skill now creates GitHub Release via gh release create",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:14:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2b8c9fa3a9bf4f2fc40f807f60f222ae0b1823c8",
          "body": "Unseen questions are now selected with probability proportional to\ndomain_weight × (1 - accuracy), so domains where the user is weakest\nreceive more questions as history builds. Domains with no history\ndefault to 0.0 accuracy (full weight preserved).\n\nAdds get_domain_accuracy() helper to selector.py\n[…]\nracy, TestAccuracyWeightedSelection). Updates walkthrough\nto reflect that plain `cert-pepper study` already self-directs toward\nweak domains.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: accuracy-weighted unseen question selection",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:07:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6328db011b16e3445fac8dd94511983286c22173",
          "body": "Add exam goal management, schedule adherence tracking, and a Rich TUI\ncalendar view showing met/partial/missed study days.\n\nNew commands:\n  cert-pepper goal set --exam-date YYYY-MM-DD [--hours 40]\n  cert-pepper goal show   (pace summary + 4-week calendar grid)\n\nKey additions:\n- user_goals schema tab\n[…]\ness on session end\n- MCP analytics: get_schedule_status tool; get_study_recommendations\n  auto-reads days_remaining from user_goals.exam_date\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: adaptive study schedule with calendar tracking",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-04T15:06:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7285f1825a8f9568c5b271baa2705481dfff4fec",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: drop --count 25 from walkthrough examples (now the default)",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T20:42:38Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "82fbf86f29c1b08207ad06c4ec64d381d6975951",
          "body": "Days 1-4 now run two plain adaptive sessions per day (--count 25, no\n--domain filter) and let the tool handle question weighting. The\ndomain-sweep table and per-domain targeting are removed. Day 5 heading\nand intro updated to match.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: replace domain-sweep days 1-4 with adaptive sessions",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T20:34:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a6e2721811efe81ea030f41f8f813af94b98b430",
          "body": "…ay sprint\n\nChange `cert-pepper study` default --count from 10 to 25 to match the\n40-hour/10-day sprint benchmark (2 sessions × 25 = 50 questions/day).\n\nRewrite walkthrough.md around a structured daily schedule: domain sweep\ndays 1–4 (D4→D2→D5→D3 by weight), weak-area drilling days 5–7, full\nadaptive day 8, mock exam day 9, due-cards-only day 10. Add upfront\nsprint requirements callout and fix table commands to include uv run.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: raise study default to 25q/session, rewrite walkthrough as 10-d…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T19:34:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4043504afddcb4b623e8bfe47237e9b077aa0606",
          "body": "- Add doc-editor skill entry to CLAUDE.md skills table\n- Clarify MCP sampling language and disclaimer copy in README.md\n- Retitle walkthrough, add exam result caveats, trim tool-name references\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add doc-editor skill, tighten README and walkthrough disclaimers",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T18:24:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4bdd8610c5b19895adedf4b28c3eaa3abe14f91b",
          "body": "- Day 0: make setup_exam via Claude Code the primary onboarding path;\n  demote db init + ingest to a manual/CLI alternative with an explicit\n  note that CONTENT_ROOT must be set before ingesting\n- Days 1-4: replace per-domain --domain flags with plain `cert-pepper\n  study`, letting the adaptive sele\n[…]\nactually type to Claude\n- Day 10: replace `# get_due_cards tool` comment with Claude Code\n  prompts; keep CLI fallback\n\nCloses #1, #2, #3, #4\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: sync walkthrough with current MCP-first UX patterns",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T17:05:59Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "504a25aa85c038e8ad5778fc6b00ba2fdeeee9cf",
          "body": "- Add compute_streak() to scorer.py: counts consecutive study days\n  ending today or yesterday (grace period), with full test coverage\n- Update progress dashboard to use compute_streak instead of a 30-day\n  COUNT DISTINCT (shows \"Study Streak\" with day/days label)\n- Fix E501 lint in connection.py migration strings (noqa suppression)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: compute_streak for study dashboard + fix E501 lint",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T15:18:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6332c5da1a0caaab93b71f2149b58f6cb74d641a",
          "body": "- Add .claude/skills/ci-watcher.md — polls crook3dfingers/cert-pepper\n  check-runs every 30s (40 iterations, 20-min cap), exits non-zero on\n  failure or timeout; mandatory background Haiku agent after every push\n- Document skill in CLAUDE.md Skills table\n- Fix PostToolUse ruff hook path (security-plus/cert-pepper → cert-pepper)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add ci-watcher skill + fix stale ruff hook path",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T15:16:33Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f2abcd9e8203f7ad924152dbd778532ce06fabae",
          "body": "Switch pyproject.toml from a static version string to hatch-vcs dynamic\nversioning. The version is now derived from git tags at build/install\ntime — no more manual pyproject.toml edits per release.\n\nAlso adds .claude/skills/release/SKILL.md: a /release skill that\ninspects the latest tag, computes the next semver bump (patch/minor/\nmajor or explicit), confirms with the user, then creates an annotated\ntag and pushes.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: dynamic versioning via hatch-vcs + /release skill",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T03:50:29Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4fd59c0548634f1a489ce67e65e77cc5e9cd87b7",
          "body": "Adds `cert-pepper upgrade` top-level command that applies DB schema\nmigrations idempotently and re-ingests study content without touching\nprogress tables (fsrs_cards, question_attempts, bkt_skill_states).\n\nAlso changes _run_migrations() to return list[str] of applied migration\nnames so callers can report what changed.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add cert-pepper upgrade command",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T03:29:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7b91fab4074e6500ba11bf1d07c6095c937ee05a",
          "body": "Replace ORDER BY weight_pct DESC LIMIT 20 with a window-function CTE\nthat picks one random unseen question per domain before weighted\nselection. Previously, any domain with >20 unseen questions monopolised\nthe entire candidate pool (Domain 4's 140 questions filled all 20 slots),\nmaking random.choice\n[…]\nion a no-op.\n\nAdds test_weighted_selection_spans_multiple_domains to confirm both a\nhigh-weight and low-weight domain appear across 50 draws.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: stratified per-domain sampling in unseen question selector",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-03T03:01:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "23c7e9b8c2b13181338e09b5794fb4ffe955c4fc",
          "body": "_fetch_reddit_excerpts queries the Reddit public JSON API (general search\n+ vendor-specific subreddit) for top posts about the exam, filtering out\nlow-score, removed, and duplicate entries.\n\n_build_research_context synthesises those excerpts via MCP sampling into\n≤15 actionable bullet points, which \n[…]\n 12 new tests cover success, network errors,\nHTTP errors, malformed JSON, empty results, score filtering, and\nremoved/deleted post filtering.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: enrich setup_exam with Reddit community research",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T16:25:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d9983b2b6cfa3f24d138165abc9c80864f7a32e6",
          "body": "… path\n\nRemove db init/ingest from Quick Start; users now open Claude Code and ask\nit to run setup_exam for their exam. Manual content path demoted to a\none-line pointer at the end of the section.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: rewrite Quick Start to use MCP setup_exam as primary onboarding…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T15:52:48Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "04a5d54abc28d3b6539eb3e73160aa01e2f4accc",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add uv install step to Quick Start",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T15:37:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c1c07e3e36ed35c1d8498620933d2ee39d0d6e02",
          "body": "Both acquisition options (release download and git clone) are now in one\nbash block — clone is commented out so the block stays copy-paste friendly\nwhile keeping the alternative visible. Removes duplicated setup lines.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: merge Quick Start into a single code block",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T14:58:07Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0f778034a525833791959efd11f9519e41c57a7f",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add latest release download to Quick Start",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T14:53:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1f4630952388d93696aec6d1fe7cd0a3b64dce1f",
          "body": "Adds a new MCP tool that returns wrong answers from a completed study or\nexam session, with per-question hints to call get_explanation(). CLI\nstudy and exam commands now print the session ID so users can ask Claude\nto explain their mistakes by session.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add get_session_wrong_answers MCP tool and session ID hints",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-03-02T14:48:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "752b0ed6481a3f487ec8362f4d189e6e62a6792d",
          "body": "- Fix all ruff errors (93 issues: import sort, unused imports, line length, f-strings)\n- Fix all mypy strict errors (41 issues: type annotations, Row guards, no-any-return)\n- Make .mcp.json portable with `uv run` commands instead of hardcoded venv paths\n- Add project metadata, classifiers, and URLs \n[…]\nml\n- Migrate dev deps from [tool.uv] to [dependency-groups] with types-PyYAML\n- Add MIT LICENSE, CHANGELOG.md, and GitHub Actions CI workflow\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: production-ready v0.1.0 release prep",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-28T03:20:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a7be1b5836404d8fdcc11b6d8bccfd6163d1a8cf",
          "body": "- Extract _parse_questions_content() helper in questions.py; add\n  parse_questions_text(text, domain_number) for parsing inline markdown\n  without a filename (used by setup_exam to ingest LLM-generated batches)\n- Add setup_exam tool to content MCP server: checks DB for existing exam\n  and returns re\n[…]\ncurity Operations sub-topics (IR, forensics, SIEM, IAM, vuln mgmt,\n  hardening, DLP, MDM, physical security, deception tech, backup/recovery)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add setup_exam MCP tool and 140 Domain 4 practice questions",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-28T02:59:53Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3a9c86b39bab1d1ccf87c021ad06b41d4df80d86",
          "body": "Multiple exams can now coexist in the same SQLite DB. Key changes:\n\n- Schema: add nullable certification_id to flashcards, acronyms,\n  study_sessions, predicted_scores; idempotent ALTER TABLE migrations\n- db/exams.py: resolve_cert_id() auto-detects single cert or accepts\n  exam_code; get_cert_id_for\n[…]\n new tests (test_exams, test_exam_yaml,\n  test_multi_exam_integration) + additions to existing suites\n  → 242 passed, 3 skipped (up from 197)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add multi-exam support",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-28T02:11:55Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "85618aacaf7ccda0b4d9e634f235ba2d9302aa6e",
          "body": "Update README and walkthrough to note that get_explanation works via\nMCP sampling in Claude Code without needing an API key. Distinguish\nCLI usage (needs key) from MCP tool usage (no key needed).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: clarify MCP sampling doesn't require ANTHROPIC_API_KEY",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-28T01:34:04Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9adca71f451fb083d059875eada2876e0a7c0714",
          "body": "…e exam\n\n- Move Python app from cert-pepper/ subdirectory to repo root\n- Move Security+ study content to examples/security-plus/\n- Update .env, .env.example, .mcp.json with new paths\n- Rewrite README.md as tool-focused documentation\n- Add docs/walkthrough.md: 10-day Security+ study guide\n- Add docs/\n[…]\nt reference\n- Add examples/security-plus/README.md\n- Update CLAUDE.md for new repo layout\n- GitHub repo renamed to crook3dfingers/cert-pepper\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Restructure: cert-pepper is the product, Security+ becomes the exampl…",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-27T22:06:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1d12fbc342a5bccfe038a8c5197a16fa9536c987",
          "body": "Includes all study content (domain notes, flashcards, practice questions,\nacronyms) and the cert-pepper adaptive study CLI (FSRS-4.5, BKT, MCP\nservers, AI explanations, progress dashboard, mock exam).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Initial commit: Security+ SY0-701 study repo with cert-pepper app",
          "author_name": "Developer",
          "author_login": null,
          "committed_at": "2026-02-27T21:39:26Z",
          "body_truncated": false,
          "is_coding_agent": true
        }
      ],
      "releases_count": 8,
      "commits_last_year": 94,
      "latest_release_at": "2026-03-16T23:21:24Z",
      "latest_release_tag": "v0.6.0",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 10,
      "days_since_latest_release": 140,
      "mean_days_between_releases": 2.4
    },
    "artifacts": {
      "collected": true,
      "structure": [],
      "declarations": [
        {
          "name": "cert-pepper",
          "path": "pyproject.toml",
          "tokens": [
            "pypi.console_scripts",
            "pypi.classifier:Environment :: Console"
          ],
          "ecosystem": "pypi"
        }
      ]
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "readme_badges": {
        "hosts": [
          "github.com",
          "shields.io"
        ],
        "total": 3,
        "header": 3,
        "collected": true,
        "has_inspect_badge": false
      },
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 87,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": []
    },
    "popularity": {
      "forks": 2,
      "stars": 5,
      "watchers": 1,
      "fork_history": {
        "days": [
          {
            "date": "2026-03-19",
            "count": 1
          },
          {
            "date": "2026-04-15",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 2,
        "total_forks": 2
      },
      "star_history": null,
      "open_issues_and_prs": 8
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "examples"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": true,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "cert_pepper/py.typed"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 39819,
      "source_files_sampled": 62,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "AGENTS.md",
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 11279
    },
    "dependencies": {
      "manifests": [
        "pyproject.toml"
      ],
      "advisories": {
        "error": null,
        "scope": "repository_graph",
        "source": "osv",
        "findings": [
          {
            "name": "mcp",
            "direct": true,
            "version": "1.26.0",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.6,
            "advisory_ids": [
              "GHSA-hvrp-rf83-w775",
              "GHSA-jpw9-pfvf-9f58",
              "GHSA-vj7q-gjh5-988w",
              "PYSEC-2026-3481",
              "PYSEC-2026-3482",
              "PYSEC-2026-3483"
            ],
            "fixed_version": "1.28.1",
            "advisory_count": 6,
            "oldest_advisory_days": 18
          },
          {
            "name": "mistune",
            "direct": true,
            "version": "3.2.1",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-2hm2-hc3v-44h9",
              "GHSA-4j32-57v6-6g45",
              "GHSA-8c25-4j27-2rv3",
              "GHSA-8mpj-m6qm-5qr8",
              "GHSA-c8j7-8cv4-2xmq",
              "GHSA-ffq3-xpv3-j92q",
              "GHSA-g97x-gvcm-x72h",
              "GHSA-qcq2-496w-v96p",
              "GHSA-qfrw-5rxm-mhh2",
              "GHSA-r4rv-85jg-w4mf"
            ],
            "fixed_version": "3.3.0",
            "advisory_count": 20,
            "oldest_advisory_days": 26
          },
          {
            "name": "pyjwt",
            "direct": true,
            "version": "2.12.1",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.4,
            "advisory_ids": [
              "GHSA-993g-76c3-p5m4",
              "GHSA-fhv5-28vv-h8m8",
              "GHSA-jq35-7prp-9v3f",
              "GHSA-w7vc-732c-9m39",
              "GHSA-xgmm-8j9v-c9wx",
              "PYSEC-2026-175",
              "PYSEC-2026-177",
              "PYSEC-2026-178",
              "PYSEC-2026-179"
            ],
            "fixed_version": "2.13.0",
            "advisory_count": 9,
            "oldest_advisory_days": 67
          },
          {
            "name": "click",
            "direct": false,
            "version": "8.3.1",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.2,
            "advisory_ids": [
              "PYSEC-2026-2132"
            ],
            "fixed_version": "8.3.3",
            "advisory_count": 1,
            "oldest_advisory_days": 95
          },
          {
            "name": "cryptography",
            "direct": false,
            "version": "46.0.7",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 8.2,
            "advisory_ids": [
              "GHSA-537c-gmf6-5ccf",
              "GHSA-g6cj-pr64-35w5",
              "GHSA-jwv3-5hgf-82ww",
              "GHSA-m2h6-j472-rp4c"
            ],
            "fixed_version": "50.0.0",
            "advisory_count": 4,
            "oldest_advisory_days": 49
          },
          {
            "name": "python-multipart",
            "direct": false,
            "version": "0.0.27",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-5rvq-cxj2-64vf",
              "GHSA-6jv3-5f52-599m",
              "GHSA-v9pg-7xvm-68hf",
              "GHSA-vffw-93wf-4j4q",
              "PYSEC-2026-3036",
              "PYSEC-2026-3037",
              "PYSEC-2026-3040",
              "PYSEC-2026-3041"
            ],
            "fixed_version": "0.0.31",
            "advisory_count": 8,
            "oldest_advisory_days": 49
          },
          {
            "name": "starlette",
            "direct": false,
            "version": "1.0.1",
            "severity": "high",
            "ecosystem": "pypi",
            "cvss_score": 7.5,
            "advisory_ids": [
              "GHSA-82w8-qh3p-5jfq",
              "GHSA-jp82-jpqv-5vv3",
              "GHSA-wqp7-x3pw-xc5r",
              "GHSA-x746-7m8f-x49c",
              "PYSEC-2026-2280",
              "PYSEC-2026-2281",
              "PYSEC-2026-248",
              "PYSEC-2026-249"
            ],
            "fixed_version": "1.3.1",
            "advisory_count": 8,
            "oldest_advisory_days": 49
          },
          {
            "name": "pydantic-settings",
            "direct": true,
            "version": "2.13.1",
            "severity": "moderate",
            "ecosystem": "pypi",
            "cvss_score": 5.3,
            "advisory_ids": [
              "GHSA-4xgf-cpjx-pc3j"
            ],
            "fixed_version": "2.14.2",
            "advisory_count": 1,
            "oldest_advisory_days": 45
          }
        ],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {
          "high": 7,
          "moderate": 1
        },
        "advisory_count": 57,
        "affected_count": 8,
        "assessed_count": 61,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 3,
        "direct_affected_count": 4
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "typer",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.12.0"
        },
        {
          "name": "rich",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=13.7.0"
        },
        {
          "name": "pydantic",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.6.0"
        },
        {
          "name": "pydantic-settings",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.2.0"
        },
        {
          "name": "sqlalchemy",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.0.0"
        },
        {
          "name": "aiosqlite",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.20.0"
        },
        {
          "name": "anthropic",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.34.0"
        },
        {
          "name": "mcp",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.0.0"
        },
        {
          "name": "mistune",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3.2.1"
        },
        {
          "name": "python-dotenv",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.2.2"
        },
        {
          "name": "pyyaml",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=6.0.0"
        },
        {
          "name": "httpx",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.27.0"
        },
        {
          "name": "pyjwt",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.12.0"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "aiosqlite",
            "direct": true,
            "version": "0.22.1",
            "ecosystem": "pypi"
          },
          {
            "name": "anthropic",
            "direct": true,
            "version": "0.84.0",
            "ecosystem": "pypi"
          },
          {
            "name": "httpx",
            "direct": true,
            "version": "0.28.1",
            "ecosystem": "pypi"
          },
          {
            "name": "mcp",
            "direct": true,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "mcp",
            "direct": true,
            "version": "1.26.0",
            "ecosystem": "pypi"
          },
          {
            "name": "mistune",
            "direct": true,
            "version": "3.2.1",
            "ecosystem": "pypi"
          },
          {
            "name": "pydantic",
            "direct": true,
            "version": "2.12.5",
            "ecosystem": "pypi"
          },
          {
            "name": "pydantic-settings",
            "direct": true,
            "version": "2.13.1",
            "ecosystem": "pypi"
          },
          {
            "name": "pyjwt",
            "direct": true,
            "version": "2.12.1",
            "ecosystem": "pypi"
          },
          {
            "name": "python-dotenv",
            "direct": true,
            "version": "1.2.2",
            "ecosystem": "pypi"
          },
          {
            "name": "pyyaml",
            "direct": true,
            "version": "6.0.3",
            "ecosystem": "pypi"
          },
          {
            "name": "rich",
            "direct": true,
            "version": "14.3.3",
            "ecosystem": "pypi"
          },
          {
            "name": "sqlalchemy",
            "direct": true,
            "version": "2.0.47",
            "ecosystem": "pypi"
          },
          {
            "name": "typer",
            "direct": true,
            "version": "0.24.1",
            "ecosystem": "pypi"
          },
          {
            "name": "annotated-doc",
            "direct": false,
            "version": "0.0.4",
            "ecosystem": "pypi"
          },
          {
            "name": "annotated-types",
            "direct": false,
            "version": "0.7.0",
            "ecosystem": "pypi"
          },
          {
            "name": "anyio",
            "direct": false,
            "version": "4.12.1",
            "ecosystem": "pypi"
          },
          {
            "name": "attrs",
            "direct": false,
            "version": "25.4.0",
            "ecosystem": "pypi"
          },
          {
            "name": "certifi",
            "direct": false,
            "version": "2026.2.25",
            "ecosystem": "pypi"
          },
          {
            "name": "cffi",
            "direct": false,
            "version": "2.0.0",
            "ecosystem": "pypi"
          },
          {
            "name": "click",
            "direct": false,
            "version": "8.3.1",
            "ecosystem": "pypi"
          },
          {
            "name": "colorama",
            "direct": false,
            "version": "0.4.6",
            "ecosystem": "pypi"
          },
          {
            "name": "coverage",
            "direct": false,
            "version": "7.13.4",
            "ecosystem": "pypi"
          },
          {
            "name": "cryptography",
            "direct": false,
            "version": "46.0.7",
            "ecosystem": "pypi"
          },
          {
            "name": "distro",
            "direct": false,
            "version": "1.9.0",
            "ecosystem": "pypi"
          },
          {
            "name": "docstring-parser",
            "direct": false,
            "version": "0.17.0",
            "ecosystem": "pypi"
          },
          {
            "name": "greenlet",
            "direct": false,
            "version": "3.3.2",
            "ecosystem": "pypi"
          },
          {
            "name": "h11",
            "direct": false,
            "version": "0.16.0",
            "ecosystem": "pypi"
          },
          {
            "name": "hatch-vcs",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "hatchling",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "httpcore",
            "direct": false,
            "version": "1.0.9",
            "ecosystem": "pypi"
          },
          {
            "name": "httpx-sse",
            "direct": false,
            "version": "0.4.3",
            "ecosystem": "pypi"
          },
          {
            "name": "idna",
            "direct": false,
            "version": "3.15",
            "ecosystem": "pypi"
          },
          {
            "name": "iniconfig",
            "direct": false,
            "version": "2.3.0",
            "ecosystem": "pypi"
          },
          {
            "name": "jiter",
            "direct": false,
            "version": "0.13.0",
            "ecosystem": "pypi"
          },
          {
            "name": "jsonschema",
            "direct": false,
            "version": "4.26.0",
            "ecosystem": "pypi"
          },
          {
            "name": "jsonschema-specifications",
            "direct": false,
            "version": "2025.9.1",
            "ecosystem": "pypi"
          },
          {
            "name": "librt",
            "direct": false,
            "version": "0.8.1",
            "ecosystem": "pypi"
          },
          {
            "name": "markdown-it-py",
            "direct": false,
            "version": "4.0.0",
            "ecosystem": "pypi"
          },
          {
            "name": "mdurl",
            "direct": false,
            "version": "0.1.2",
            "ecosystem": "pypi"
          },
          {
            "name": "mypy",
            "direct": false,
            "version": "1.19.1",
            "ecosystem": "pypi"
          },
          {
            "name": "mypy-extensions",
            "direct": false,
            "version": "1.1.0",
            "ecosystem": "pypi"
          },
          {
            "name": "packaging",
            "direct": false,
            "version": "26.0",
            "ecosystem": "pypi"
          },
          {
            "name": "pathspec",
            "direct": false,
            "version": "1.0.4",
            "ecosystem": "pypi"
          },
          {
            "name": "pluggy",
            "direct": false,
            "version": "1.6.0",
            "ecosystem": "pypi"
          },
          {
            "name": "pycparser",
            "direct": false,
            "version": "3.0",
            "ecosystem": "pypi"
          },
          {
            "name": "pydantic-core",
            "direct": false,
            "version": "2.41.5",
            "ecosystem": "pypi"
          },
          {
            "name": "pygments",
            "direct": false,
            "version": "2.20.0",
            "ecosystem": "pypi"
          },
          {
            "name": "pytest",
            "direct": false,
            "version": "9.0.3",
            "ecosystem": "pypi"
          },
          {
            "name": "pytest-asyncio",
            "direct": false,
            "version": "1.3.0",
            "ecosystem": "pypi"
          },
          {
            "name": "pytest-cov",
            "direct": false,
            "version": "7.0.0",
            "ecosystem": "pypi"
          },
          {
            "name": "python-multipart",
            "direct": false,
            "version": "0.0.27",
            "ecosystem": "pypi"
          },
          {
            "name": "pywin32",
            "direct": false,
            "version": "311",
            "ecosystem": "pypi"
          },
          {
            "name": "referencing",
            "direct": false,
            "version": "0.37.0",
            "ecosystem": "pypi"
          },
          {
            "name": "rpds-py",
            "direct": false,
            "version": "0.30.0",
            "ecosystem": "pypi"
          },
          {
            "name": "ruff",
            "direct": false,
            "version": "0.15.4",
            "ecosystem": "pypi"
          },
          {
            "name": "shellingham",
            "direct": false,
            "version": "1.5.4",
            "ecosystem": "pypi"
          },
          {
            "name": "sniffio",
            "direct": false,
            "version": "1.3.1",
            "ecosystem": "pypi"
          },
          {
            "name": "sse-starlette",
            "direct": false,
            "version": "3.2.0",
            "ecosystem": "pypi"
          },
          {
            "name": "starlette",
            "direct": false,
            "version": "1.0.1",
            "ecosystem": "pypi"
          },
          {
            "name": "types-pyyaml",
            "direct": false,
            "version": "6.0.12.20250915",
            "ecosystem": "pypi"
          },
          {
            "name": "typing-extensions",
            "direct": false,
            "version": "4.15.0",
            "ecosystem": "pypi"
          },
          {
            "name": "typing-inspection",
            "direct": false,
            "version": "0.4.2",
            "ecosystem": "pypi"
          },
          {
            "name": "uvicorn",
            "direct": false,
            "version": "0.41.0",
            "ecosystem": "pypi"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 64,
        "direct_count": 14,
        "indirect_count": 50
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 8,
        "merged_prs": 23,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 12,
        "closed_unmerged_prs": 24
      },
      "bus_factor": 1,
      "bot_contributors": 1,
      "top_contributors": [
        {
          "type": "User",
          "login": "crook3dfingers",
          "commits": 6,
          "avatar_url": "https://avatars.githubusercontent.com/u/58201909?v=4"
        },
        {
          "type": "User",
          "login": "securitychops",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/39027035?v=4"
        }
      ],
      "contributors_sampled": 2,
      "top_contributor_share": 0.857
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "codeql.yml",
        "gitleaks.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "uv.lock"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 8,
            "reason": "branch protection is not maximal on development and all release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 10,
            "reason": "19 out of 19 merged PRs checked by a CI test -- score normalized to 10",
            "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 1/14 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "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": 10,
            "reason": "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": "13 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": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 10,
            "reason": "all dependencies are pinned",
            "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": "32 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "1a9360050a4727fc34bccbbce0ff96b71005bcb7",
        "ran_at": "2026-08-04T00:35:57Z",
        "aggregate_score": 6.3,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": true,
      "has_security_policy": true,
      "has_dependabot_config": true
    },
    "contribution_flow": {
      "collected": true,
      "recent_prs": {
        "merged_7d": 0,
        "decided_7d": 0,
        "merged_30d": 0,
        "authors_30d": 0,
        "decided_30d": 0,
        "sample_size": 47,
        "window_days": 30,
        "sample_exhausted": false,
        "authors_probed_30d": 0,
        "newcomer_merged_30d": 0,
        "bot_prs_excluded_30d": 19,
        "newcomer_authors_30d": 0,
        "newcomer_decided_30d": 0
      },
      "ci_last_run_at": "2026-08-03T22:48:47Z",
      "oldest_open_prs": [
        {
          "number": 40,
          "created_at": "2026-05-29T18:20:25Z",
          "last_comment_at": "2026-05-29T18:46:39Z",
          "last_comment_author": "crook3dfingers"
        },
        {
          "number": 45,
          "created_at": "2026-06-08T22:45:30Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 46,
          "created_at": "2026-06-16T03:08:42Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 61,
          "created_at": "2026-07-20T22:44:44Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 65,
          "created_at": "2026-07-27T22:44:28Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 67,
          "created_at": "2026-08-03T22:45:53Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 68,
          "created_at": "2026-08-03T22:47:22Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 69,
          "created_at": "2026-08-03T22:48:42Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-06-04T18:25:03Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/cert-pepper/cert-pepper",
    "host": "github.com",
    "name": "cert-pepper",
    "owner": "cert-pepper"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": "The weighted overall 60 is calibrated to 65 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 60,
            "calibrated": 65,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 65,
      "inputs": {
        "security": 63,
        "vitality": 79,
        "community": 48,
        "governance": 37,
        "calibration": "2026-08-02",
        "engineering": 71,
        "ai_readiness": 75,
        "weighted_overall_raw": 60
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 79,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "commits_last_year": 94,
              "human_commit_share": 0.787,
              "days_since_last_push": 0,
              "active_weeks_last_year": 10
            },
            "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": "10/52 weeks with commits",
                "points": 6.9,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 10
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "94 commits in the last year",
                "points": 17.8,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 94
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "13 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": 90,
            "inputs": {
              "releases_count": 8,
              "latest_release_tag": "v0.6.0",
              "releases_from_tags": false,
              "days_since_latest_release": 140,
              "mean_days_between_releases": 2.4
            },
            "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 140 days ago",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 140
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~2.4 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 2.4
                    }
                  }
                ],
                "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": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "weak",
        "name": "Community & Adoption",
        "value": 48,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "forks": 2,
              "stars": 5,
              "watchers": 1,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "5 stars",
                "points": 9.8,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "2 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "1 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "excellent",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 92,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "readme_badges": 3,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "readme_badge_services": [
                "github.com",
                "shields.io"
              ],
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "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": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "weak",
        "name": "Sustainability & Governance",
        "value": 37,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 15,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 2,
              "top_contributor_share": 0.857
            },
            "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 86% of commits",
                "points": 3.2,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 86
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "2 contributors",
                "points": 2.7,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Newcomer PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "newcomer_pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 65,
            "inputs": {
              "merged_prs": 23,
              "open_issues": 0,
              "closed_issues": 12,
              "prs_merged_7d": 0,
              "prs_decided_7d": 0,
              "prs_merged_30d": 0,
              "prs_decided_30d": 0,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 24,
              "first_time_authors_30d": 0,
              "first_time_prs_merged_30d": 0,
              "first_time_prs_decided_30d": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "100% of issues closed",
                "points": 42,
                "status": "met",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "23/47 decided PRs merged",
                "points": 14.7,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 23,
                      "decided": 47
                    }
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "no first-time contributor's PR decided in 30d",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_newcomer_prs",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 1/14 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "weak",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 36,
            "inputs": {
              "followers": 0,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "cert-pepper",
              "public_repos": 5,
              "account_age_days": 140
            },
            "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": "0 followers of cert-pepper",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 0,
                      "login": "cert-pepper"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "5 public repos, account ~0 yr old",
                "points": 6.4,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 5
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 0
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 71,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 68,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "19 out of 19 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "topics": [
                "adaptive-learning",
                "certification",
                "claude",
                "cli",
                "education",
                "exam-prep",
                "flashcards",
                "fsrs",
                "mcp",
                "python",
                "security-plus",
                "spaced-repetition",
                "study",
                "comptia"
              ],
              "has_wiki": false,
              "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": "14 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 14
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 63,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 63,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 6.3
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection is not maximal on development and all release branches",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "19 out of 19 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "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 1/14 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "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": "update tool detected",
                "points": 7.5,
                "status": "met",
                "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": "13 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 not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 5,
                "status": "met",
                "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": "32 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "good",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories. Remaining weights renormalized. Matched 61 resolved dependencies against OSV; 3 could not be assessed (no resolved version, an unsupported ecosystem, or beyond the reported package list). This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "indirect_dependencies_free_of_known_advisories"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_repository",
                "params": {
                  "assessed": 61
                }
              },
              {
                "code": "advisories_unassessed",
                "params": {
                  "count": 3
                }
              },
              {
                "code": "advisories_repo_graph_caveat",
                "params": {}
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 65,
            "inputs": {
              "source": "osv",
              "advisories": 57,
              "affected_packages": 8,
              "assessed_packages": 61,
              "unassessed_packages": 3,
              "affected_by_severity": "high 7, moderate 1",
              "direct_affected_packages": 4
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "4 affected: mcp 1.26.0 (high 7.6), mistune 3.2.1 (high 7.5), pyjwt 2.12.1 (high 7.4), +1 more",
                "points": 9.1,
                "status": "partial",
                "details": [
                  {
                    "code": "advisories_affected",
                    "params": {
                      "count": 4,
                      "packages": "mcp 1.26.0 (high 7.6), mistune 3.2.1 (high 7.5), pyjwt 2.12.1 (high 7.4)"
                    }
                  },
                  {
                    "code": "advisories_affected_more",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "transitive set not separable from development and test dependencies in this scope",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_scope_not_separable",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory has been public longer than 90 days",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "advisories_none_stale",
                    "params": {
                      "days": 90
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "exceptional",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 61,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "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": 75,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.932,
              "agent_instruction_files": [
                "AGENTS.md",
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 11279
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "AGENTS.md, CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "AGENTS.md, 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": "69 of 74 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 69,
                      "sampled": 74
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "uv.lock"
              ],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [
                "cert_pepper/py.typed"
              ],
              "agent_commit_share": 0.649,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0.213
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "cert_pepper/py.typed",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "cert_pepper/py.typed"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "61 of the last 94 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 61,
                      "sampled": 94
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "20 of the last 94 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 20,
                      "sampled": 94
                    }
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 39819,
              "source_files_sampled": 62,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (cert_pepper/py.typed)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "cert_pepper/py.typed",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/62 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 62,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "moderate",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 60,
            "inputs": {
              "example_dirs": [
                "examples"
              ],
              "has_mcp_signal": true,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "examples",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "examples"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "top": [
        "application"
      ],
      "labels": [
        "cli",
        "mcp-server"
      ],
      "scores": {
        "cli": 24,
        "mcp-server": 7
      },
      "primary": "cli",
      "evidence": [
        {
          "tier": "declared",
          "label": "cli",
          "source": "pypi.console_scripts",
          "weight": 10
        },
        {
          "tier": "declared",
          "label": "cli",
          "source": "pypi.classifier:Environment :: Console",
          "weight": 6
        },
        {
          "tier": "dependencies",
          "label": "cli",
          "source": "dep:typer",
          "weight": 4
        },
        {
          "tier": "dependencies",
          "label": "mcp-server",
          "source": "dep:mcp",
          "weight": 4
        },
        {
          "tier": "structure",
          "label": "mcp-server",
          "source": "mcp_signal",
          "weight": 3
        },
        {
          "tier": "description",
          "label": "cli",
          "source": "description:cli",
          "weight": 2
        },
        {
          "tier": "tags",
          "label": "cli",
          "source": "tag:cli",
          "weight": 2
        }
      ],
      "artifacts": [
        {
          "path": "pyproject.toml",
          "labels": [
            "cli"
          ],
          "ecosystem": "pypi"
        }
      ],
      "confidence": "high",
      "host_extension": false,
      "runs_as_process": true,
      "consumed_by_code": false
    },
    "metrics_version": "2.5.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "Could not fetch pypi package 'cert-pepper' from its registry"
  ],
  "report_type": "repository",
  "generated_at": "2026-08-04T00:36:09.802359Z",
  "schema_version": "0.30.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/cert-pepper/cert-pepper.svg",
  "full_name": "cert-pepper/cert-pepper",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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