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

vertti / ci-snitch

CI performance intelligence tool — detect outliers, regressions, flaky pipelines, and cost hotspots in GitHub Actions

GoMIT★ 7 зірок⑂ 0 форківз квіт. 2026 р.Переглянути на GitHub ↗

vertti/ci-snitch має індекс здоров’я 62 зі 100, що відповідає смузі «Помірний». Найвищий показник — AI Readiness (90/100), найнижчий — Community & Adoption (30/100). Останнє оновлення було 1 день тому. Більшість нещодавньої роботи виконує один учасник.

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

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

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

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

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

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

Власність

Janne SinivirtaОсобистий обліковий запис
94 підписники41 публічний репозиторійз січ. 2011 р.Hedera Dx

Цей репозиторій належить особистому обліковому запису. Проєкт з єдиним власником несе більший ризик безперервності, ніж підтримуваний організацією.

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

РеєстрПакетВерсіяЗавантажень / місВерсіїОстання публікація
Gogithub.com/vertti/ci-snitchv0.28.0-304 дні тому

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

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

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

80Добрий · 22% загального індексу
Як обчислюється оцінка
36/36Свіжість push — останній push 1 дн. тому
9/36Ритм комітів — 13/52 тижнів із комітами
18/18Обсяг комітів — 458 комітів за останній рік
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Використані вхідні дані
commits_last_year458
human_commit_share0,97
days_since_last_push1
active_weeks_last_year13
Як обчислюється оцінка
27/27Випускає релізи — опубліковано 29 релізів
36/36Свіжість релізів — останній реліз 4 дн. тому
27/27Ритм релізів — реліз кожні ~10,1 дн.
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Використані вхідні дані
releases_count29
latest_release_tagv0.28.0
releases_from_tagsні
days_since_latest_release4
mean_days_between_releases10,1

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

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

30У зоні ризику · 18% загального індексу
Як обчислюється оцінка
12.6/60Зірки — 7 зірок
0/25Форки — 0 форків
0/15Спостерігачі — 0 спостерігачів
Використані вхідні дані
forks0
stars7
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonbelow_threshold
Як обчислюється оцінка
22.5/22.5README
22.5/22.5Ліцензія — визнана ліцензія (MIT)
0/18Настанови CONTRIBUTING
0/13.5Кодекс поведінки
0/7.2Шаблон issue
0/6.3Шаблон PR
Використані вхідні дані
has_readmeтак
has_licenseтак
has_contributingні
has_issue_templateні
has_code_of_conductні
has_pull_request_templateні

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

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

49У зоні ризику · 24% загального індексу
Як обчислюється оцінка
9/54Бас-фактор — на 1 контриб’ютор(ів) припадає половина всіх комітів
0/22.5Розподіл комітів — головний контриб’ютор — автор 100% комітів
1.4/13.5Широта контриб’юторів — 1 контриб’юторів
6/10OpenSSF Scorecard: Contributors — project has 2 contributing companies or organizations -- score normalized to 6
Використані вхідні дані
bus_factor1
contributors_sampled1
top_contributor_share1
Як обчислюється оцінка
0/46.8Вирішення issue — закрито 0% issue
38.2/38.3Прийняття PR — злито 196/196 вирішених PR
0/15OpenSSF Scorecard: Code-Review — Found 0/13 approved changesets -- score normalized to 0
Використані вхідні дані
merged_prs196
open_issues1
closed_issues0
issue_closed_ratio0
closed_unmerged_prs0
Як обчислюється оцінка
10/30Підтримка власника — особистий (користувацький) обліковий запис
0/20Верифікований домен — не застосовно до користувацьких облікових записів
14.2/25Охоплення власника — 94 підписників у vertti
23.8/25Послужний список — 41 публічних репозиторіїв, вік облікового запису ~15 р.
Використані вхідні дані
followers94
owner_typeUser
is_verified
owner_loginvertti
public_repos41
account_age_days5 670
Виключено з оцінювання (немає даних або не застосовно): Верифікований домен. Залишкові ваги перенормовано.

Супровід пакетів

100Відмінний
Як обчислюється оцінка
25/25Опубліковано й доступно — 1 пакет(ів) у go
35/35Свіжість публікацій — остання публікація 4 дн. тому
20/20Історія версій — 30 опублікованих версій
20/20Не застарілий — активний, не deprecated і не yanked
Використані вхідні дані
packagesgithub.com/vertti/ci-snitch
ecosystemsgo
any_deprecatedні
min_days_since_publish4

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

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

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

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

85Відмінний
Як обчислюється оцінка
30/30README
25/25Каталог документації
0/15Сайт документації / домашня сторінка
10/10Опис репозиторію
10/10Теми — 7 тем
10/10Wiki
Використані вхідні дані
topicsci, cli, devops, github-actions, golang, observability, performance
has_wikiтак
homepage
has_readmeтак
has_docs_dirтак
has_descriptionтак

Безпека

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

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

Стан безпеки

63Помірний
Як обчислюється оцінка
7.5/7.5Binary-Artifacts — no binaries found in the repo
2.2/7.5Branch-Protection — branch protection is not maximal on development and all release branches
2.5/2.5CI-Tests — 16 out of 16 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 0/13 approved changesets -- score normalized to 0
1.5/2.5Contributors — project has 2 contributing companies or organizations -- score normalized to 6
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 — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
5/5Packaging — packaging workflow detected
5/5Pinned-Dependencies — all dependencies are pinned
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
7.5/7.5Token-Permissions — GitHub workflow tokens follow principle of least privilege
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Використані вхідні дані
sourceopenssf_scorecard
checks_evaluated18
scorecard_versionv5.5.0
checks_inconclusive0
scorecard_aggregate6,3

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

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

90Відмінний · 0% загального індексу
Як обчислюється оцінка
45/45Інструкції для агентів — CLAUDE.md
0/15Машиночитана документація (llms.txt)
40/40Читабельна історія комітів — намір зазначено у 83 з 97 людських комітів (структурований заголовок або пояснювальний текст)
Використані вхідні дані
has_llms_txtні
legible_history_share0,856
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes1 221
Як обчислюється оцінка
18/18Розгортання однією командою — mise.toml
22/22Автоматизовані тести
11/11Конфігурація лінтера / форматера — .golangci.yml
11/11Статична перевірка типів — Go (статично типізована)
10/10Відтворюване середовище — lockfile
0/10Підтверджена практика роботи з агентами — серед останніх 100 комітів немає створених агентом
8/8Автоматизоване супроводження — 3 з останніх 100 комітів — автоматичні оновлення залежностей
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Використані вхідні дані
has_nixні
has_testsтак
lockfilesgo.sum
has_dockerfileні
typed_languageтак
bootstrap_filesmise.toml
has_devcontainerні
has_linter_configтак
typecheck_configs
agent_commit_share0
toolchain_manifestsgo.mod
dependency_bot_commit_share0,03
Як обчислюється оцінка
45/45Типізований код — Go (статично типізована)
55/55Керовані розміри файлів — 0/76 файлів вихідного коду понад 60 КБ
Використані вхідні дані
primary_languageGo
largest_source_bytes26 427
source_files_sampled76
oversized_source_files0

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

7зірок GitHub
1контриб'юторів
458комітів за останні 12 місяців
1днів від останнього пушу
29релізів
1бас-фактор
1відкритих issue
Goпакетних екосистем

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

  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Докладніше

Історія зірок і форків 7 ★ / 0 ⇿
7Зірки
7Релізи

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

234567722026-042026-042026-04
Мажорні 0Мінорні 7Патчі 0
OpenSSF Scorecard 6.3 / 10
6.3сукупно

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

10Binary-Artifactsno binaries found in the repo
3Branch-Protectionbranch protection is not maximal on development and all release branches
10CI-Tests16 out of 16 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 0/13 approved changesets -- score normalized to 0
6Contributorsproject has 2 contributing companies or organizations -- score normalized to 6
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
10Packagingpackaging workflow detected
10Pinned-Dependenciesall dependencies are pinned
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
10Token-PermissionsGitHub workflow tokens follow principle of least privilege
10Vulnerabilities0 existing vulnerabilities detected
Прямі залежності 6
РеєстрПакетОбмеження версіїМаніфест
Gogithub.com/google/go-github/v89v89.0.0go.mod
Gogithub.com/mattn/go-isattyv0.0.23go.mod
Gogithub.com/spf13/cobrav1.10.2go.mod
Gogithub.com/stretchr/testifyv1.11.1go.mod
Gogolang.org/x/syncv0.22.0go.mod
Gomodernc.org/sqlitev1.54.0go.mod
Усі залежності не зібрано

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

Звіт у форматі JSON машиночитний
{
  "data": {
    "repo": {
      "topics": [
        "ci",
        "cli",
        "devops",
        "github-actions",
        "golang",
        "observability",
        "performance"
      ],
      "is_fork": false,
      "size_kb": 892,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "Go": 490147,
        "Shell": 2244
      },
      "pushed_at": "2026-07-20T20:51:37Z",
      "created_at": "2026-04-09T10:41:06Z",
      "owner_type": "User",
      "updated_at": "2026-07-17T06:56:15Z",
      "description": "CI performance intelligence tool — detect outliers, regressions, flaky pipelines, and cost hotspots in GitHub Actions",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Go",
      "significant_languages": [
        "Go"
      ]
    },
    "owner": {
      "blog": null,
      "name": "Janne Sinivirta",
      "type": "User",
      "login": "vertti",
      "company": "Hedera Dx",
      "location": "Finland",
      "followers": 94,
      "avatar_url": "https://avatars.githubusercontent.com/u/557751?v=4",
      "created_at": "2011-01-11T16:34:52Z",
      "is_verified": null,
      "public_repos": 41,
      "account_age_days": 5670
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.28.0",
          "kind": "minor",
          "published_at": "2026-07-17T07:04:41Z"
        },
        {
          "tag": "v0.27.0",
          "kind": "minor",
          "published_at": "2026-07-16T06:10:23Z"
        },
        {
          "tag": "v0.26.0",
          "kind": "minor",
          "published_at": "2026-07-15T16:29:11Z"
        },
        {
          "tag": "v0.25.0",
          "kind": "minor",
          "published_at": "2026-07-15T09:29:41Z"
        },
        {
          "tag": "v0.24.0",
          "kind": "minor",
          "published_at": "2026-07-15T08:57:33Z"
        },
        {
          "tag": "v0.23.0",
          "kind": "minor",
          "published_at": "2026-04-24T08:51:36Z"
        },
        {
          "tag": "v0.22.0",
          "kind": "minor",
          "published_at": "2026-04-21T07:06:57Z"
        },
        {
          "tag": "v0.21.0",
          "kind": "minor",
          "published_at": "2026-04-20T12:49:20Z"
        },
        {
          "tag": "v0.20.0",
          "kind": "minor",
          "published_at": "2026-04-17T09:21:15Z"
        },
        {
          "tag": "v0.19.0",
          "kind": "minor",
          "published_at": "2026-04-17T09:06:59Z"
        },
        {
          "tag": "v0.18.0",
          "kind": "minor",
          "published_at": "2026-04-17T08:25:51Z"
        },
        {
          "tag": "v0.17.0",
          "kind": "minor",
          "published_at": "2026-04-16T11:56:03Z"
        },
        {
          "tag": "v0.16.0",
          "kind": "minor",
          "published_at": "2026-04-16T07:21:37Z"
        },
        {
          "tag": "v0.15.0",
          "kind": "minor",
          "published_at": "2026-04-15T17:25:57Z"
        },
        {
          "tag": "v0.14.0",
          "kind": "minor",
          "published_at": "2026-04-15T07:08:45Z"
        },
        {
          "tag": "v0.13.0",
          "kind": "minor",
          "published_at": "2026-04-14T07:38:07Z"
        },
        {
          "tag": "v0.12.0",
          "kind": "minor",
          "published_at": "2026-04-13T09:04:07Z"
        },
        {
          "tag": "v0.11.0",
          "kind": "minor",
          "published_at": "2026-04-13T08:53:25Z"
        },
        {
          "tag": "v0.10.0",
          "kind": "minor",
          "published_at": "2026-04-13T08:25:01Z"
        },
        {
          "tag": "v0.9.0",
          "kind": "minor",
          "published_at": "2026-04-13T06:45:17Z"
        },
        {
          "tag": "v0.8.1",
          "kind": "patch",
          "published_at": "2026-04-13T06:38:43Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2026-04-13T06:14:06Z"
        },
        {
          "tag": "v0.7.0",
          "kind": "minor",
          "published_at": "2026-04-13T05:27:45Z"
        },
        {
          "tag": "v0.6.0",
          "kind": "minor",
          "published_at": "2026-04-13T04:56:26Z"
        },
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2026-04-12T19:14:33Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2026-04-12T18:41:54Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-04-12T16:19:23Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-04-12T08:09:39Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-04-10T10:50:11Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "027ba8cdae0dd55285969ad2681dfa6a3cbe6643",
          "body": "Golden-file tests for formatters; palette struct replaces global ANSI state",
          "is_bot": false,
          "headline": "Merge pull request #197 from vertti/test/output-golden-files",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:56:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2b77f24f0ba2a6c20514548eaf272635db5571ec",
          "body": "… state",
          "is_bot": false,
          "headline": "Golden-file tests for formatters; palette struct replaces global ANSI…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:54:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea02fc47dd187db646795ca37f80ad5480a6777f",
          "body": "App: build cache index once; drop REST hydration from WorkflowFetcher",
          "is_bot": false,
          "headline": "Merge pull request #196 from vertti/refactor/app-cache-index",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:46:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0bf6dc19f198dfb1c321ddd4b7f219d4a0449f1c",
          "body": null,
          "is_bot": false,
          "headline": "App: build cache index once; drop REST hydration from WorkflowFetcher",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:42:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3f23597a17e08bf7e4457098716e7c0c95da0480",
          "body": "Test hygiene: fetcher helper, vacuous-pass fixes, realistic migration schema",
          "is_bot": false,
          "headline": "Merge pull request #195 from vertti/test/hygiene-batch",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:39:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f7074035c8dbdb8130f5ae62f7ed3c8e219484ea",
          "body": "… schema",
          "is_bot": false,
          "headline": "Test hygiene: fetcher helper, vacuous-pass fixes, realistic migration…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:38:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "08d20c9b649650dbf90fe9ba0db61ba7227a8043",
          "body": "Vestigial sweep: markdown -v, dead code, misplaced docs, uniform writers",
          "is_bot": false,
          "headline": "Merge pull request #194 from vertti/chore/vestigial-sweep",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:34:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "06d70c3a5839eb6a05945e3f83220b5cc70c1a90",
          "body": null,
          "is_bot": false,
          "headline": "Vestigial sweep: markdown -v, dead code, misplaced docs, uniform writers",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:33:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c087799793db397e6ce1198e06a04f6a10069a9b",
          "body": "Share run-category constants across preprocess, app, and CLI",
          "is_bot": false,
          "headline": "Merge pull request #193 from vertti/refactor/preprocess-event-category",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:27:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6200f653da864a1ddc1f03b3d382dd4566e385f6",
          "body": null,
          "is_bot": false,
          "headline": "Share run-category constants across preprocess, app, and CLI",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:26:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dbbb4ecc2fba97ac097ec3836a397de0c099ea47",
          "body": "Export analyze label constants; use them in formatters",
          "is_bot": false,
          "headline": "Merge pull request #192 from vertti/refactor/exported-label-constants",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:24:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "513a6fd32d930ab8519c2be7e8e159ce35b4f421",
          "body": "Update jdx/mise-action digest to dad1bfd",
          "is_bot": false,
          "headline": "Merge pull request #184 from vertti/renovate/jdx-mise-action-digest",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:23:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bfefa0089004b95a4446f4f7011a40069bea4bd6",
          "body": "Update module modernc.org/sqlite to v1.54.0",
          "is_bot": false,
          "headline": "Merge pull request #183 from vertti/renovate/modernc.org-sqlite-1.x",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:23:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b31e5f5da4a14b7e9e19ff78202e146c0344808",
          "body": "…ty-0.x\n\nUpdate module github.com/mattn/go-isatty to v0.0.23",
          "is_bot": false,
          "headline": "Merge pull request #181 from vertti/renovate/github.com-mattn-go-isat…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:23:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7062146c260a4388000d8869929986d99ca320a3",
          "body": null,
          "is_bot": false,
          "headline": "Export analyze label constants; use them in formatters",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:22:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "088865dd702c33d575d83fe56d599f9339e652f4",
          "body": "Delete deprecated Warning aliases; use diag.Diagnostic directly",
          "is_bot": false,
          "headline": "Merge pull request #191 from vertti/refactor/finish-diag-migration",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:21:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d2ad0412cd681464e715bd4126b879dd1ebf13a1",
          "body": null,
          "is_bot": false,
          "headline": "Delete deprecated Warning aliases; use diag.Diagnostic directly",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:19:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "512b0ede2018b0f8e5a3d7590ada3afcf9ac1f96",
          "body": "Share per-(workflow, job) duration series via AnalysisContext.JobSeries",
          "is_bot": false,
          "headline": "Merge pull request #190 from vertti/refactor/shared-job-series",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:19:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "48ee47f552fab35e3d18464eaa8abd40e56e95b1",
          "body": null,
          "is_bot": false,
          "headline": "Share per-(workflow, job) duration series via AnalysisContext.JobSeries",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:18:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "872632c367fd65c6b221723b3f74010d34d26a70",
          "body": "Classify API errors on every REST path; extract waitForRateReset",
          "is_bot": false,
          "headline": "Merge pull request #189 from vertti/fix/github-error-classification",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:14:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bdda4afc23974c5c6d2402b2b6e40426aa25838c",
          "body": "Cost: price unknown GitHub images by OS prefix; collapse Model type",
          "is_bot": false,
          "headline": "Merge pull request #188 from vertti/fix/cost-os-prefix-fallback",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:14:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4193c423c055af746f7c6b14b5b7f0bf26cfcb0f",
          "body": null,
          "is_bot": false,
          "headline": "Classify API errors on every REST path; extract waitForRateReset",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:09:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cea4c8e153e99310f3ea183f18ccf2dfe5f22f0a",
          "body": null,
          "is_bot": false,
          "headline": "Cost: price unknown GitHub images by OS prefix; collapse Model type",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:06:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9974ccd8e4f757da77ba056d33abb2d50f47d4ce",
          "body": "Store: single-item save/load as wrappers over batch paths",
          "is_bot": false,
          "headline": "Merge pull request #187 from vertti/refactor/store-dedup",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:01:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ee9e1af2944ecb816d5b1dcf99f38c025b47950",
          "body": null,
          "is_bot": false,
          "headline": "Store: single-item save/load as wrappers over batch paths",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T06:00:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f11a3eb85861821eab7ffce256ede76d314fc6fe",
          "body": "Unify formatter decision logic; guard finding-type bucketing",
          "is_bot": false,
          "headline": "Merge pull request #186 from vertti/fix/formatter-decision-logic",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T05:57:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c0cf58f4dd4a0007143f94e600361d873e4d5eb7",
          "body": null,
          "is_bot": false,
          "headline": "Unify formatter decision logic; guard finding-type bucketing",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T05:55:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "99f53ea0b2c7a1a7dff204c70ca680ffba950111",
          "body": "Add Q section to ROADMAP: code-quality review findings",
          "is_bot": false,
          "headline": "Merge pull request #185 from vertti/chore/quality-roadmap",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T05:53:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e70025eed997576640819b849d60bf1827c39dba",
          "body": null,
          "is_bot": false,
          "headline": "Add Q section: code-quality review findings",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-17T05:51:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "562f4f3fca10848ec27fd461d7c06884aea7212b",
          "body": null,
          "is_bot": true,
          "headline": "Update jdx/mise-action digest to dad1bfd",
          "author_name": "renovate[bot]",
          "author_login": "renovate[bot]",
          "committed_at": "2026-07-16T19:47:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "34f3abcadaff1d4c8573322f5280aeca5fe18127",
          "body": null,
          "is_bot": true,
          "headline": "Update module modernc.org/sqlite to v1.54.0",
          "author_name": "renovate[bot]",
          "author_login": "renovate[bot]",
          "committed_at": "2026-07-16T09:27:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ae4a511641cb00760d38eb135692413bbeee090",
          "body": "Document release-notes rewrite step in CLAUDE.md",
          "is_bot": false,
          "headline": "Merge pull request #182 from vertti/docs/release-notes-rewrite",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T07:44:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "406e7ec288ff0269148a691c64df92f693307b2f",
          "body": null,
          "is_bot": false,
          "headline": "Document release-notes rewrite step in CLAUDE.md",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T07:42:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1731a5920abb49fe0be7305c9d74df251f70b363",
          "body": "Serialize release runs per tag",
          "is_bot": false,
          "headline": "Merge pull request #180 from vertti/ci/release-concurrency",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T07:20:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "460beee2b8ae1ac96326de85b54303aac36e8a8a",
          "body": null,
          "is_bot": true,
          "headline": "Update module github.com/mattn/go-isatty to v0.0.23",
          "author_name": "renovate[bot]",
          "author_login": "renovate[bot]",
          "committed_at": "2026-07-16T07:19:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a0b75fa880a4fe543bd2ea1081ce6ff6fa533cc6",
          "body": "GitHub delivered the v0.27.0 tag push twice, starting two Release runs\ntwo seconds apart; the goreleaser twins raced and the loser failed with\n'422: tag_name already_exists' (the release itself published fine via\nthe winner). A concurrency group with cancel-in-progress makes a\nduplicate delivery cancel its sibling instead of racing it.",
          "is_bot": false,
          "headline": "Serialize release runs per tag",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T07:18:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e689873d1c3f52b8098b3e6746e7d57f3b448a49",
          "body": "Note F3 premise validation requirement",
          "is_bot": false,
          "headline": "Merge pull request #179 from vertti/chore/f3-premise-note",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T06:06:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ce1c074fc42fe598135d3198d65b36a870d5fb0",
          "body": "workflow_call callees do not create separate runs — their jobs execute\ninside the caller's run — so the claimed caller/callee finding\nduplication only occurs when a callee also runs standalone. Validate\nagainst a real repo before building the M-sized dedup.",
          "is_bot": false,
          "headline": "Note F3 premise validation requirement",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T06:05:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e70703343fbc896fd4c280f0f002446d718de8a5",
          "body": "Annotate regressions with commit context: ci-config vs code",
          "is_bot": false,
          "headline": "Merge pull request #178 from vertti/feat/changepoint-commit-context",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T06:04:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d6d3046810139643305c9c113f9a5c454b64b748",
          "body": "Confirmed regressions are enriched post-analysis with their commit's\nchanged-file stats and a classification: ci-config (touched\n.github/workflows/) vs code. A workflow-file change is the first\nsuspect for a CI regression — this labels it immediately instead of\nsending the user to the commit page.\n\n\n[…]\n-14), so\n  CI config is the first suspect'; markdown gets a bold CI-config\n  marker\n\nThe SHA-keyed SQLite cache from the original sketch is deferred until\nlookup volume ever matters. (ROADMAP F2 + F5)",
          "is_bot": false,
          "headline": "Annotate regressions with commit context: ci-config vs code",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T06:03:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bb30c81cd90876f5be0097d7db22c38586fd09e3",
          "body": "Add --branch-category to separate PR runs from default-branch traffic",
          "is_bot": false,
          "headline": "Merge pull request #177 from vertti/feat/branch-category",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:59:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f764cb375508bb59f3299e6ebcb08e8b141e2e25",
          "body": "PR-branch failures (expected during development) and default-branch\nfailures (incidents) carry different signal. --branch-category selects\nruns by trigger event before ALL analysis: pr keeps pull_request runs,\nmain keeps everything else (pushes, schedules, dispatches). Unlike\n--branch it catches eve\n[…]\nth --branch. The --branch/--category\nfilters now share one applyRunFilters seam (also fixes a just-\nintroduced date-layout typo caught in review: 2026-01-02 as a Go time\nlayout). (ROADMAP F4, was 5.4)",
          "is_bot": false,
          "headline": "Add --branch-category to separate PR runs from default-branch traffic",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:58:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "de804cc241a0b8b5c78ffd391b374bfc55f80254",
          "body": "Estimate parallelization savings for sequential pipeline stages",
          "is_bot": false,
          "headline": "Merge pull request #176 from vertti/feat/parallelism-savings",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:54:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7b9a73bef9f7086d15e0f1dc5c81cb2bdcb8731c",
          "body": "For each sequential stage transition, PotentialSavings = min(prev\nduration, stage duration) — the wall-clock upper bound of removing the\nwait (dur(A)+dur(B) -> max(A,B)). Thanks to F8, sequential now means\nthe stage genuinely started after the previous ended, so the estimate\nrests on honest data.\n\nS\n[…]\nute, and the LLM\nbriefing suggests it with 'verify job dependencies first'. No\ndependency guessing from job names — the number is explicitly an upper\nbound, not a recommendation. (ROADMAP F1, was 5.1)",
          "is_bot": false,
          "headline": "Estimate parallelization savings for sequential pipeline stages",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:52:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e73f2d0e9194f622e84f55dbe12e3edd619f96d1",
          "body": "Exclude re-run attempts from the duration series",
          "is_bot": false,
          "headline": "Merge pull request #175 from vertti/fix/rerun-duration-pollution",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:49:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9f9fe0e4abca48d2abd7d8e7d726e33766c33d1a",
          "body": "Dedup keeps the latest attempt; for 're-run failed jobs' that attempt's\nwall clock covers only the re-run subset — a 40-minute workflow could\ncontribute a 6-minute 'duration' to the summary, outlier, and\nchangepoint series. preprocess.Run now drops RunAttempt > 1 from the\nduration series with an agg\n[…]\n failure, rerun-tax, and cost analysis (where their\nminutes are real). The full-pipeline test expectation is updated: the\ndeduped attempt-2 run no longer masquerades as a duration sample.\n(ROADMAP F9)",
          "is_bot": false,
          "headline": "Exclude re-run attempts from the duration series",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:48:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "72282b290b7b5fee47f12dd7c1c1ae1968933fd0",
          "body": "Group pipeline stages by temporal overlap, not start proximity",
          "is_bot": false,
          "headline": "Merge pull request #174 from vertti/fix/stage-detection-overlap",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:45:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ca9950efd5610516f556194dd2484c7dcadf729",
          "body": "Jobs joined a stage only if they started within 30s of the stage's\nfirst job. Two failure modes:\n- a matrix fan-out staggered by a constrained runner pool (starts\n  minutes apart, all running concurrently) became several fake\n  'sequential' stages, corrupting stage count, critical path,\n  parallelis\n[…]\nnd = new stage. This also makes the aggregated\nSequential flag correct by construction — a stage break can only\nhappen once the previous stage finished. Unblocks F1 (parallelism\nsavings). (ROADMAP F8)",
          "is_bot": false,
          "headline": "Group pipeline stages by temporal overlap, not start proximity",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-16T05:43:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "343580cd33115be44738d1c0312421338220b45f",
          "body": "Add ci-snitch doctor",
          "is_bot": false,
          "headline": "Merge pull request #173 from vertti/feat/doctor-command",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:42:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0560085bc2a47344f783f18ae86832d4d4b2623c",
          "body": "Validates the environment with one line per check: token resolution,\nGitHub API reachability (reports both rate pools), cache database\nopenable, and git remote detection (informational — not being in a\nrepo is a normal way to run the tool). All checks run even after a\nfailure so the report is complete; any hard failure exits non-zero.\n\nCompletes the H section (H11 stays deliberately deferred).\n(ROADMAP H4, was 7.3)",
          "is_bot": false,
          "headline": "Add ci-snitch doctor",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:41:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8acb7e214d3119599e81bc2bcaa13d27483a2ae9",
          "body": "Hygiene batch 3: README catch-up, refuse Windows in install.sh",
          "is_bot": false,
          "headline": "Merge pull request #172 from vertti/chore/hygiene-batch-3",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:39:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1eecd1c5b95dfdb8ba3036bdb3630a18c4c3e4e0",
          "body": "- H10: README flags table gains --fail-on, --raw-output, and -q; new\n  CI-gating section documents exit-code semantics (0 clean, 1 error,\n  2 gate tripped); md alias, shell completion, NO_COLOR documented.\n- H5: install.sh refuses Windows explicitly with a pointer to the\n  release zip — the advertised path could never succeed (the archive\n  holds ci-snitch.exe; the script assumed /usr/local/bin and sudo).\n\n(ROADMAP H5, H10)",
          "is_bot": false,
          "headline": "Hygiene batch 3: README catch-up, refuse Windows in install.sh",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:37:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "90e50f2b0c52912be5f67862771192b5edec4249",
          "body": "Hygiene batch 2: govulncheck in CI, real smoke pipeline, drop redundant step",
          "is_bot": false,
          "headline": "Merge pull request #171 from vertti/chore/hygiene-batch-2",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:36:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d112183b127ac0b27e13f0877b9e6dbd895265d5",
          "body": "…nt step\n\n- H3: 'mise run vuln' (govulncheck) + fatal CI step. The local baseline\n  check was blocked by network flakiness; this PR's own CI run is the\n  baseline gate — auto-merge cannot fire if it is dirty.\n- H6: smoke hydrates via FetchRunDetailsGraphQL (the production path;\n  it exercised only t\n[…]\n run.\n- H8: the 'Test schema migration' CI step re-ran TestMigration already\n  covered by 'mise run test'; dropped. The migration unit tests build\n  genuine old-schema databases.\n\n(ROADMAP H3, H6, H8)",
          "is_bot": false,
          "headline": "Hygiene batch 2: govulncheck in CI, real smoke pipeline, drop redunda…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:34:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "31ea2561fa3cd89f9100b7c3f453075764cbea88",
          "body": "Hygiene batch 1: gitignore, mise ordering, goreleaser formats, store cleanups",
          "is_bot": false,
          "headline": "Merge pull request #170 from vertti/chore/hygiene-batch-1",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:30:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d444cad5f1ab5a238e47e0aa48f67dc74820aee6",
          "body": "…cleanups\n\n- H1: local/ (untracked scratch with real repo data — one git add -A\n  from violating the anonymization rule) and *.out gitignored\n- H2: lint depends on fmt so 'mise run check' can't race the formatter\n  against the linter\n- H7: goreleaser 'formats:' replaces the format keys deprecated si\n[…]\nuns_status dropped (its only query is an\n  inequality) from schema and existing DBs. Cache read failures now log\n  a warning instead of silently degrading to a full re-fetch.\n\n(ROADMAP H1, H2, H7, H9)",
          "is_bot": false,
          "headline": "Hygiene batch 1: gitignore, mise ordering, goreleaser formats, store …",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:28:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "808ea89abbf4ff6943bb47596d950f2ed6cf7f6f",
          "body": "Complete truncated GraphQL runs via REST",
          "is_bot": false,
          "headline": "Merge pull request #169 from vertti/perf/complete-truncated-runs",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:25:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37886e96ed4b04919475515fd4fc2e7a6a6fcb03",
          "body": "Runs exceeding the GraphQL page size (>50 jobs or >50 steps per job)\nwere marked truncated, analyzed with partial data, and never cached —\nre-fetched incomplete on every scan.\n\ncompleteTruncated refetches them via REST, which paginates jobs fully\nand embeds complete steps: one bounded REST call per \n[…]\nEST fails, the run stays\ntruncated/uncacheable with the old warning.\n\nSimpler than the originally planned after:-cursor pagination with the\nsame outcome. Completes the P section. (ROADMAP P2, was 4.2)",
          "is_bot": false,
          "headline": "Complete truncated GraphQL runs via REST",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:23:34Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a264deaab3202820ad206658c6ce1ce1784f1e53",
          "body": "Align listing windows with day boundaries",
          "is_bot": false,
          "headline": "Merge pull request #168 from vertti/perf/window-alignment",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:20:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4c009e3af8d2fb9365fde7172a35f5cfc80d967e",
          "body": "P3: windowStart = windowEnd with an inclusive date-only created filter\nput the seam day in BOTH windows — boundary runs were double-listed,\ndouble-hydrated, double-counted by the rate budget, and double-saved\n(~4 duplicated days per default 30d scan). The next window now starts\nthe day after the pre\n[…]\never.\n--since 0d is still rejected (validated before truncation).\n\nLive: two consecutive 7d scans of cli/cli went from a steady '21 to\nfetch' on every run to 808 cached / 0 to fetch.\n(ROADMAP P3 + P4)",
          "is_bot": false,
          "headline": "Align listing windows with day boundaries",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:18:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f9b11d2eacb6534b911c3e6cd10fac50f615f2dd",
          "body": "Batch cache hydration: 3 queries instead of ~1500",
          "is_bot": false,
          "headline": "Merge pull request #167 from vertti/perf/batch-cache-hydration",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:15:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6aabf0b4128dc64cf0abed0f7e964f393ed766ac",
          "body": "LoadRunDetails looped LoadRunDetail per run, which looped loadSteps per\njob: a 500-run cached scan issued roughly 1 + 500 + sum(jobs) queries.\n\nLoadRunDetailsByIDs hydrates the requested runs in three IN-clause\nqueries per 500-ID chunk (runs, jobs WHERE run_id IN, steps WHERE\njob_id IN) assembled wi\n[…]\n degrading to a fetch. LoadRunDetails is\nreimplemented on top, so the smoke harness benefits too.\n\nBenchmark (500 runs, 1 job, 2 steps): 13.2ms -> 3.3ms per full load,\n4x faster. (ROADMAP P1, was 4.1)",
          "is_bot": false,
          "headline": "Batch cache hydration: 3 queries instead of ~1500",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:14:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d0fb1063b1c827ec84d195464b60a0771df7041a",
          "body": "Add --fail-on CI gating with exit code 2",
          "is_bot": false,
          "headline": "Merge pull request #166 from vertti/feat/fail-on-gating",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:08:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c221a6633d857b9490e584731df3ad335394e032",
          "body": "--fail-on regression,failure-rate>N turns ci-snitch into a CI gate:\nexit 2 when conditions match findings (distinct from 1 = operational\nerror so pipelines can tell 'the data tripped the gate' from 'the tool\nbroke'), with one fail-on: reason per tripped finding on stderr —\nprinted even in quiet mode, since telling CI why is the whole point.\nConditions are validated before any network call.\n\nCompletes the U section. (ROADMAP U6)",
          "is_bot": false,
          "headline": "Add --fail-on CI gating with exit code 2",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:06:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4013e40aa9f49ab9fc7a452e95134404ad5f1f83",
          "body": "Markdown format parity: failures, cost, pipeline, runners, steps",
          "is_bot": false,
          "headline": "Merge pull request #165 from vertti/feat/markdown-parity",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T14:01:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3f6a19af661c2f658f1e85b4cc685989c0c277d9",
          "body": "Markdown rendered only summaries, non-info changepoints, and outliers\nwhile the README markets it for PR comments alongside failure/cost/\npipeline features — all of which were silently missing. Adds Failure\nRates, Cost, Pipeline Structure (with critical-path marker), Runner\nSizing, and Step-Level Timing sections; all names pipe-escaped.\nPrerequisite for the F7 PR-comment bot. (ROADMAP U4)",
          "is_bot": false,
          "headline": "Markdown format parity: failures, cost, pipeline, runners, steps",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:59:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "318fc8aef83010075fc812fcbe96361a632f4779",
          "body": "LLM format: deterministic ordering, caveats, glossary, scoped hints",
          "is_bot": false,
          "headline": "Merge pull request #164 from vertti/fix/llm-format-quality",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:55:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "592052242879215cf7112f152c0b3e79f5e7033d",
          "body": "- categoryBreakdown ties broke on map iteration order and the\n  ByConclusion max-pick flapped between runs on identical data — both\n  now tie-break lexicographically (pinned by run-50-times tests)\n- new '## Data Caveats' section narrates result diagnostics: an LLM\n  acting on the numbers must know w\n[…]\nlow A's docker\n  step was attributable to workflow B's same-named job\n- [COST] priorities use the same PriorityScore >= 50 bar as the\n  suggestions section instead of unconditional top-3\n\n(ROADMAP U5)",
          "is_bot": false,
          "headline": "LLM format: deterministic ordering, caveats, glossary, scoped hints",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:54:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8ba5927e411ca614f735a4101256a8839c102604",
          "body": "Output polish: nine paper cuts across formatters and CLI",
          "is_bot": false,
          "headline": "Merge pull request #163 from vertti/fix/output-polish-batch",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:49:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ce6c28a4e34130207828799cb3ceb7a5158e4185",
          "body": "- fmtDur renders hours as hours ('2h30m', was '150m')\n- pipes escaped in markdown/LLM table names (a job named 'a|b' broke\n  the table)\n- JSON emits \"diagnostics\": [] and \"findings\": [] instead of null\n  (fixed at the formatter boundary so directly-built results are\n  covered too) — null broke jq '.\n[…]\nr (progress, diagnostics, timing)\n- --format gets shell completion values\n- --since 0d and future dates rejected before any API call\n- compactResult comment matches what the code filters\n\n(ROADMAP U7)",
          "is_bot": false,
          "headline": "Output polish: nine paper cuts across formatters and CLI",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:48:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b353b7ff81595930af61d9450ec89724f59dd1b9",
          "body": "Record active filters in result meta",
          "is_bot": false,
          "headline": "Merge pull request #162 from vertti/feat/filter-context-in-meta",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:41:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0911905a560a6f4061b4554b0c9dacfa496cc7b2",
          "body": "ResultMeta gains branch/workflow (omitempty) and since. A JSON or LLM\nconsumer comparing two reports could not previously tell whether one\nwas branch- or workflow-scoped — a --branch main report and an\nall-branches report looked interchangeable. (ROADMAP U3)",
          "is_bot": false,
          "headline": "Record active filters in result meta",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:39:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8a9f1f6b541daf6c45c56667e78cf2e1eff785c2",
          "body": "Make common failure errors actionable",
          "is_bot": false,
          "headline": "Merge pull request #161 from vertti/fix/actionable-errors",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:38:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1528c5826cb0ea375f36e23e43b09d9f89c40fcf",
          "body": "- 404/401/403 from workflow listing map to guidance: check the\n  spelling / private repos need a token with access; token invalid or\n  expired (gh auth login); scope or SAML SSO authorization. Previously:\n  'GET https://... 404 Not Found []'.\n- A --workflow filter matching zero workflows errors imme\n[…]\ne no-runs error names an active --workflow filter.\n- The CLI wires a logger into the client so rate-limit sleeps and REST\n  fallbacks print instead of hanging silently for up to an hour.\n\n(ROADMAP U2)",
          "is_bot": false,
          "headline": "Make common failure errors actionable",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:36:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e583f6aafde02d31bf7bd8e968424b6684a9d1ad",
          "body": "Cover pipeline/runner tables, raw-output file, and Progress",
          "is_bot": false,
          "headline": "Merge pull request #160 from vertti/test/formatter-coverage",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:31:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "03eaa45f236ff84ebf625ec41f82c8c49e3d9cfa",
          "body": "Completes T6 (and the T section): richTestResult gains pipeline and\nrunner findings — writePipelineTable and writeRunnerTable were at 0%\ndespite rendering primary features; --raw-output gets a temp-file test\n(valid JSON, briefing points at the file); Progress is tested through\na stderr pipe exercising the real constructor's non-TTY branch with a\nno-ANSI assertion. internal/output 68% -> 78%. (ROADMAP T6)",
          "is_bot": false,
          "headline": "Cover pipeline/runner tables, raw-output file, and Progress",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:30:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "133b8664a8b31dae5f43d42270e75587f97e0524",
          "body": "Unit-test the GraphQL conversion functions",
          "is_bot": false,
          "headline": "Merge pull request #159 from vertti/test/graphql-unit-coverage",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:27:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2efb898d192c1f215382f86b7ad2d32d258fb1c1",
          "body": "Completes T2: table tests for parseGraphQLTime (nil/empty/garbage\ndegrade to zero time), graphqlConclusion (uppercase enum -> REST\nlowercase, nil for in-progress), convertGraphQLJobs (IDs, parent run\nID, status normalization, steps), and truncateBody. Together with the\nD4-D8 behavior tests, internal/github goes from 42% at review time\n(graphql.go at 0%) to 87.5%. (ROADMAP T2)",
          "is_bot": false,
          "headline": "Unit-test the GraphQL conversion functions",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:26:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "29e49bb09923b7aaa4cec0db3a6649cc24ec609b",
          "body": "Add race detector to CI; test auth bootstrap and subprocess helper",
          "is_bot": false,
          "headline": "Merge pull request #158 from vertti/test/race-ci-auth-exec",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:25:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fb7a2f5268180821b19a29616e84c02979405b13",
          "body": "T4: mise run test-race task + CI step — the client runs bounded-\nconcurrency goroutines and errgroup fan-outs; the race suite passed\nlocally throughout the fix wave but nothing kept it that way.\n\nT5: internal/system and the auth bootstrap were at 0% coverage despite\nbeing the first code every real i\n[…]\nd happy paths\nvia fake gh scripts on PATH; exec_test covers stdout/not-found/stderr/\ntimeout. Deadline kills now report 'timed out' instead of the opaque\n'signal: killed' (red-first). (ROADMAP T4, T5)",
          "is_bot": false,
          "headline": "Add race detector to CI; test auth bootstrap and subprocess helper",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:23:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "894554240cf6db55c1f15f98b4741abf0be8402c",
          "body": "Analyzer labeling hygiene: six small correctness fixes",
          "is_bot": false,
          "headline": "Merge pull request #157 from vertti/fix/analyzer-labeling-hygiene",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:21:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61cc7c96fba307a9dd16ea090b9b880e54d6894c",
          "body": "Completes A14 (and with it the entire A section):\n\n- skipped runs excluded from the failure-rate denominator (they never\n  executed; path-filtered workflows produce many, deflating real rates)\n- 'neutral' is a deliberate non-failing conclusion, no longer a failure\n- FailureKind is 'unknown' when the\n[…]\nnt fallback 4 -> 3 (current arm64 runners)\n- step-timing sort keyed per (workflow, job); JobCostBreakdown splits\n  SelfHostedMinutes out of a field named billable_minutes\n\n(ROADMAP A14, analyzer half)",
          "is_bot": false,
          "headline": "Analyzer labeling hygiene: six small correctness fixes",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:20:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f1cae4d2473720cd1dd11a3ecf85ae055326065b",
          "body": "Fix tie handling and discreteness in Mann-Whitney p-values",
          "is_bot": false,
          "headline": "Merge pull request #156 from vertti/fix/stats-hygiene",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:15:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bcda8cec0f631a45945644556426c53abbe9b526",
          "body": "Three hygiene fixes in the significance machinery (stats half of A14):\n\n- The exact path enumerated rank positions 1..n as if the data were\n  untied, while the observed U used average ranks. Second-resolution CI\n  durations tie constantly, and the mismatch was anti-conservative:\n  measured p=0.31 wh\n[…]\n  (U is discrete).\n\nNot adopted: tie-corrected sigma for the normal path — needs both\nsegments >20 runs with heavy ties (rare) and corrects in the\nmore-significant direction. (ROADMAP A14, stats half)",
          "is_bot": false,
          "headline": "Fix tie handling and discreteness in Mann-Whitney p-values",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:14:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fe5ae91f7c172e214716d21f0227791aaa1e0c51",
          "body": "…mples\n\nCompare failure trend against the prior period; gate volatility labels on sample size",
          "is_bot": false,
          "headline": "Merge pull request #155 from vertti/fix/trend-and-volatility-small-sa…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:09:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e4311387440cf43847560ec183322299d39e51fe",
          "body": "…s on sample size\n\nA11: recentRate (last 7d) was compared against the overall rate that\nINCLUDES those 7 days, halving the observable signal (a 12%->20%\nweek-over-week rise diluted to +0.04, under the 0.05 trigger), and with\n--since 7d the two windows were identical so the trend was structurally\nalw\n[…]\nords the release-verification gate in the roadmap (workflow\nconclusion must be success; release-page existence is not a signal —\nlearned from the v0.24.0/v0.25.0 tap-push failures). (ROADMAP A11, A12)",
          "is_bot": false,
          "headline": "Compare failure trend against the prior period; gate volatility label…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T13:08:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c7d2a33475465e3bf5216202c054b88adfd5d78e",
          "body": "Price each run by its own labels in cost accumulation",
          "is_bot": false,
          "headline": "Merge pull request #154 from vertti/fix/per-run-cost-multiplier",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:41:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5e565d3aec1e12cacd95eb089b767f256b04a3fc",
          "body": "jobCosts froze multiplier/isSelfHosted from the first run encountered\n(in map iteration order, not even chronological): a job migrated\nmid-window — e.g. ubuntu-latest to self-hosted — had every run priced\nat whichever variant happened to be seen first, either billing free\nself-hosted minutes or zeroing out billable ones.\n\nIsSelfHosted/LookupMultiplier now run per run; the job breakdown's\ndisplayed multiplier tracks the most recent run by CreatedAt.\n(ROADMAP A13)",
          "is_bot": false,
          "headline": "Price each run by its own labels in cost accumulation",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:40:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "80430c209c1736a21d2bf9d1e33a4ed975268ae1",
          "body": "Apply Benjamini-Hochberg correction across change-point p-values",
          "is_bot": false,
          "headline": "Merge pull request #153 from vertti/fix/bh-correction",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:39:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7c6cb7a77b57e16e81ba6439a273050137b54593",
          "body": "One Mann-Whitney test per change point across dozens of jobs at\nalpha=0.05 guarantees ~1 false 'significant regression' per 20 stable\njobs — statistically inevitable noise presented as findings.\n\nstats.BenjaminiHochberg converts the batch's p-values to FDR-adjusted\nq-values in post-processing; findi\n[…]\nQValue is\nexported in ChangePointDetail JSON.\n\nLive validation on cli/cli: 6 of 52 change points had raw p < 0.05 but\nq > 0.05 — the predicted per-batch false-positive rate, now demoted.\n(ROADMAP A10)",
          "is_bot": false,
          "headline": "Apply Benjamini-Hochberg correction across change-point p-values",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:38:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e17b2d4e3006397315352c6c9e27f940fb9013ef",
          "body": "Report CUSUM change onset instead of lagged detection index",
          "is_bot": false,
          "headline": "Merge pull request #152 from vertti/fix/cusum-onset-backtracking",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:36:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "20199008068c28c62da56e50f8f7d127748df715",
          "body": "The CUSUM alarm fires several samples after a moderate shift begins;\nthose samples landed in the 'before' segment, biasing BeforeMean,\nshrinking PctChange, and attributing the change to the wrong\ncommit/date — the run at the detection index, not the run where the\nchange started.\n\nThe reported index \n[…]\nter point directly before the shift\nsits above mu+k with that fixture's tiny variance and is locally\nindistinguishable from the shift's first point. The classification\nstays inconclusive. (ROADMAP A9)",
          "is_bot": false,
          "headline": "Report CUSUM change onset instead of lagged detection index",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:35:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "df5658dbf1c8ba6d7608879721efb000ce87f528",
          "body": "Skip queue-time stats for re-run attempts",
          "is_bot": false,
          "headline": "Merge pull request #151 from vertti/fix/queue-time-rerun-attempts",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:30:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "78a969fe10ad155855297e3fe18c84dcf24aa848",
          "body": "Queue time is StartedAt - CreatedAt, but GitHub resets run_started_at\nto the latest attempt's start while created_at stays at original\ncreation: a run re-run the next morning contributed a fake ~12h queue\ntime that exploded the p95. Only first attempts count — an attempt-2\ngap measures 'time until someone clicked re-run'. (ROADMAP A6)",
          "is_bot": false,
          "headline": "Skip queue-time stats for re-run attempts",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:30:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a8c21d0cfe7e1024b2b2b1dba8017aed9d64bd2d",
          "body": "Unify runner core-count parsing across sizing advice and cost",
          "is_bot": false,
          "headline": "Merge pull request #150 from vertti/fix/unified-core-count-parsing",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:28:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3d91fd16004fe71d54d84cd52e46a87179adcb2f",
          "body": "Mirror-image parsers: runners.go split labels on '-' so GitHub's\ncanonical ubuntu-latest-16-cores fell through to the 2-core default —\na 16-core runner with a >15min job got 'undersized: consider larger\nrunner' advice. Meanwhile cost's regex only matched the split\nconvention, so ubuntu-22.04-32core \n[…]\nbels like\nblacksmith-16vcpu-* are not billed at invented GitHub rates.\n\nAlso creates runners_test.go — the runner analyzer had no tests at\nall, which is how the wrong-advice bug survived. (ROADMAP A5)",
          "is_bot": false,
          "headline": "Unify runner core-count parsing across sizing advice and cost",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:28:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "46878405e253646c3e22587c28a8d74f00aa820c",
          "body": "…-reads\n\nGraphQL: fetch latest-attempt check runs; bound body reads",
          "is_bot": false,
          "headline": "Merge pull request #149 from vertti/fix/graphql-latest-filter-bounded…",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:25:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4a456ce91a390c2f9973ca03b298b884b363fe68",
          "body": "- checkRuns now uses filterBy:{checkType:LATEST}, matching REST's\n  filter=latest: re-run runs hydrated via GraphQL no longer carry\n  duplicate old-attempt jobs that skewed job/step stats and made the\n  two hydration paths disagree. Query verified against the live API.\n- doGraphQL reads error bodies\n[…]\nards\n  success responses at 64MiB (an oversized payload fails JSON parse and\n  takes the D4 REST fallback). Completes old roadmap item 3.6.\n\nCompletes the D (data integrity) section. (ROADMAP D8 + D9)",
          "is_bot": false,
          "headline": "GraphQL: fetch latest-attempt check runs; bound body reads",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:24:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "60aad2f6f36569b0fab1b18fdb20713927ea7e79",
          "body": "Abort on cancellation instead of degrading to partial analysis",
          "is_bot": false,
          "headline": "Merge pull request #148 from vertti/fix/cancellation-aborts",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:22:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8a45e85c61b6b3d129cd144c0d8671881b8f19af",
          "body": "Cancellation mid-hydration previously produced a normal-looking\nanalysis of whatever subset had hydrated: fetchBatchGraphQL fell back\nto REST on ctx errors, FetchRunDetails converted per-run ctx errors\ninto 'failed to fetch jobs' warnings (one Ctrl+C manufactured 45 of\nthem in the red test), hydrate\n[…]\nn returns the context error\n- main uses signal.NotifyContext(SIGINT, SIGTERM) + ExecuteContext\n\nVerified live: SIGINT mid-run exits 1 with 'context canceled', no\ntruncated analysis table. (ROADMAP D7)",
          "is_bot": false,
          "headline": "Abort on cancellation instead of degrading to partial analysis",
          "author_name": "Janne Sinivirta",
          "author_login": "vertti",
          "committed_at": "2026-07-15T09:22:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 29,
      "commits_last_year": 458,
      "latest_release_at": "2026-07-17T07:04:41Z",
      "latest_release_tag": "v0.28.0",
      "releases_from_tags": false,
      "days_since_last_push": 1,
      "active_weeks_last_year": 13,
      "days_since_latest_release": 4,
      "mean_days_between_releases": 10.1
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 42,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/vertti/ci-snitch",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/vertti/ci-snitch",
          "is_deprecated": false,
          "latest_version": "v0.28.0",
          "repository_url": "https://github.com/vertti/ci-snitch",
          "versions_count": 30,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-07-17T06:56:05Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 4
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 7,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": {
        "days": [
          {
            "date": "2026-04-16",
            "count": 2
          },
          {
            "date": "2026-04-17",
            "count": 1
          },
          {
            "date": "2026-04-18",
            "count": 2
          },
          {
            "date": "2026-04-20",
            "count": 1
          },
          {
            "date": "2026-04-24",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 7,
        "total_stars": 7
      },
      "open_issues_and_prs": 2
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "mise.toml"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 26427,
      "source_files_sampled": 76,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 1221
    },
    "dependencies": {
      "manifests": [
        "go.mod"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "go"
      ],
      "dependencies": [
        {
          "name": "github.com/google/go-github/v89",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v89.0.0"
        },
        {
          "name": "github.com/mattn/go-isatty",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.0.23"
        },
        {
          "name": "github.com/spf13/cobra",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.10.2"
        },
        {
          "name": "github.com/stretchr/testify",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.11.1"
        },
        {
          "name": "golang.org/x/sync",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.22.0"
        },
        {
          "name": "modernc.org/sqlite",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.54.0"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 1,
        "merged_prs": 196,
        "open_issues": 1,
        "closed_ratio": 0,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 1,
      "top_contributors": [
        {
          "type": "User",
          "login": "vertti",
          "commits": 418,
          "avatar_url": "https://avatars.githubusercontent.com/u/557751?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "release.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        ".golangci.yml"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "go.sum"
      ],
      "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": 3,
            "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": "16 out of 16 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 0/13 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 6,
            "reason": "project has 2 contributing companies or organizations -- score normalized to 6",
            "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": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 10,
            "reason": "all dependencies are pinned",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 10,
            "reason": "GitHub workflow tokens follow principle of least privilege",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "027ba8cdae0dd55285969ad2681dfa6a3cbe6643",
        "ran_at": "2026-07-22T02:32:56Z",
        "aggregate_score": 6.3,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-17T07:04:45Z",
      "oldest_open_prs": [
        {
          "number": 198,
          "created_at": "2026-07-20T20:51:34Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-07-17T06:56:06Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 36,
          "created_at": "2026-04-13T05:41:03Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/vertti/ci-snitch",
    "host": "github.com",
    "name": "ci-snitch",
    "owner": "vertti"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 62,
      "inputs": {
        "security": 63,
        "vitality": 80,
        "community": 30,
        "governance": 49,
        "engineering": 84
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 80,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 73,
            "inputs": {
              "commits_last_year": 458,
              "human_commit_share": 0.97,
              "days_since_last_push": 1,
              "active_weeks_last_year": 13
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 1 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 1
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "13/52 weeks with commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 13
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "458 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 458
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "releases_count": 29,
              "latest_release_tag": "v0.28.0",
              "releases_from_tags": false,
              "days_since_latest_release": 4,
              "mean_days_between_releases": 10.1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "29 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 29
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 4 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 4
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~10.1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 10.1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "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": "at_risk",
        "name": "Community & Adoption",
        "value": 30,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 13,
            "inputs": {
              "forks": 0,
              "stars": 7,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "below_threshold"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "7 stars",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (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": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 49,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 16,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "at_risk",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 38,
            "inputs": {
              "merged_prs": 196,
              "open_issues": 1,
              "closed_issues": 0,
              "issue_closed_ratio": 0,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "0% of issues closed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 0
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "196/196 decided PRs merged",
                "points": 38.2,
                "status": "met",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 196,
                      "decided": 196
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/13 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 60,
            "inputs": {
              "followers": 94,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "vertti",
              "public_repos": 41,
              "account_age_days": 5670
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "personal (user) account",
                "points": 10,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_personal",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": "not applicable to user accounts",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_applicable_to_user_accounts",
                    "params": {}
                  }
                ],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "94 followers of vertti",
                "points": 14.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 94,
                      "login": "vertti"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "41 public repos, account ~15 yr old",
                "points": 23.8,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 41
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 15
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "github.com/vertti/ci-snitch"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 4
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on go",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "go"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 4 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 4
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "30 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 30
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 84,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 84,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": ".golangci.yml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "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": "16 out of 16 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "excellent",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "topics": [
                "ci",
                "cli",
                "devops",
                "github-actions",
                "golang",
                "observability",
                "performance"
              ],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "7 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 63,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": null,
            "notes": [],
            "value": 63,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 18,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 0,
              "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": 2.2,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "16 out of 16 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 0/13 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 1.5,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "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": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "GitHub workflow tokens follow principle of least privilege",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 2
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "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": "excellent",
        "name": "AI Readiness",
        "value": 90,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.856,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 1221
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "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": "83 of 97 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 83,
                      "sampled": 97
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "excellent",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [
                "mise.toml"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [
                "go.mod"
              ],
              "dependency_bot_commit_share": 0.03
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "mise.toml",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "mise.toml"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".golangci.yml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Go (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "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": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "3 of the last 100 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 3,
                      "sampled": 100
                    }
                  }
                ],
                "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": 100,
            "inputs": {
              "primary_language": "Go",
              "largest_source_bytes": 26427,
              "source_files_sampled": 76,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Go (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/76 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 76,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-22T02:33:13.021807Z",
  "schema_version": "0.26.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/v/vertti/ci-snitch.svg",
  "full_name": "vertti/ci-snitch",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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