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

Yolean / y-cluster

Go · ShellЛіцензію не виявлено★ 0 зірок⑂ 0 форківз квіт. 2026 р.Переглянути на GitHub ↗

Yolean/y-cluster має індекс здоров’я 50 зі 100, що відповідає смузі «Помірний». Найвищий показник — Vitality (73/100), найнижчий — Community & Adoption (12/100). Останнє оновлення було 8 днів тому. Більшість нещодавньої роботи виконує один учасник.

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

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

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

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

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

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

Власність

YoleanОрганізація
9 підписників99 публічних репозиторіївз лют. 2015 р.

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

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

РеєстрПакетВерсіяЗавантажень / місВерсіїОстання публікація
Gogithub.com/Yolean/y-clusterv0.5.0-208 днів тому

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

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

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

73Добрий · 22% загального індексу
Як обчислюється оцінка
28.8/36Свіжість push — останній push 8 дн. тому
4.2/36Ритм комітів — 6/52 тижнів із комітами
18/18Обсяг комітів — 181 комітів за останній рік
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Використані вхідні дані
commits_last_year181
human_commit_share1
days_since_last_push8
active_weeks_last_year6
Як обчислюється оцінка
27/27Випускає релізи — опубліковано 20 релізів
36/36Свіжість релізів — останній реліз 8 дн. тому
27/27Ритм релізів — реліз кожні ~7,8 дн.
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Використані вхідні дані
releases_count20
latest_release_tagv0.5.0
releases_from_tagsні
days_since_latest_release8
mean_days_between_releases7,8

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

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

12Критичний · 18% загального індексу
Як обчислюється оцінка
0/60Зірки — 0 зірок
0/25Форки — 0 форків
0/15Спостерігачі — 0 спостерігачів
Використані вхідні дані
forks0
stars0
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Як обчислюється оцінка
22.5/22.5README
0/22.5Ліцензія — файлу ліцензії не виявлено
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ні

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

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

51Помірний · 24% загального індексу
Як обчислюється оцінка
9/54Бас-фактор — на 1 контриб’ютор(ів) припадає половина всіх комітів
0/22.5Розподіл комітів — головний контриб’ютор — автор 100% комітів
1.4/13.5Широта контриб’юторів — 1 контриб’юторів
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Використані вхідні дані
bus_factor1
contributors_sampled1
top_contributor_share1
Як обчислюється оцінка
15.6/46.8Вирішення issue — закрито 33% issue
35.4/38.3Прийняття PR — злито 25/27 вирішених PR
0/15OpenSSF Scorecard: Code-Review — Found 0/1 approved changesets -- score normalized to 0
Використані вхідні дані
merged_prs25
open_issues2
closed_issues1
issue_closed_ratio0,333
closed_unmerged_prs2
Як обчислюється оцінка
30/30Підтримка власника — у власності організації
0/20Верифікований домен
7.2/25Охоплення власника — 9 підписників у Yolean
25/25Послужний список — 99 публічних репозиторіїв, вік облікового запису ~11 р.
Використані вхідні дані
followers9
owner_typeOrganization
is_verified
owner_loginYolean
public_repos99
account_age_days4 165

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

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

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

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

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

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

40У зоні ризику
Як обчислюється оцінка
30/30README
0/25Каталог документації
0/15Сайт документації / домашня сторінка
0/10Опис репозиторію
0/10Теми
10/10Wiki
Використані вхідні дані
topics
has_wikiтак
homepage
has_readmeтак
has_docs_dirні
has_descriptionні

Безпека

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

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

Стан безпеки

38У зоні ризику
Як обчислюється оцінка
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
2.5/2.5CI-Tests — 1 out of 1 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/1 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
0/2.5Ліцензія — license file not detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — немає даних
5/5Pinned-Dependencies — all dependencies are pinned
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.
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6/7.5Vulnerabilities — 2 existing vulnerabilities detected
Використані вхідні дані
sourceopenssf_scorecard
checks_evaluated17
scorecard_versionv5.5.0
checks_inconclusive1
scorecard_aggregate3,9
Виключено з оцінювання (немає даних або не застосовно): packaging. Залишкові ваги перенормовано.

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

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

68Помірний · 0% загального індексу
Як обчислюється оцінка
0/45Інструкції для агентів — немає CLAUDE.md / AGENTS.md / правил редактора
0/15Машиночитана документація (llms.txt)
40/40Читабельна історія комітів — намір зазначено у 93 з 100 людських комітів (структурований заголовок або пояснювальний текст)
Використані вхідні дані
has_llms_txtні
legible_history_share0,93
agent_instruction_files
agent_instruction_max_bytes
Як обчислюється оцінка
12.6/18Розгортання однією командою — go.mod (домовленість інструментарію, без раннера задач)
22/22Автоматизовані тести
11/11Конфігурація лінтера / форматера — .golangci.yaml
11/11Статична перевірка типів — Go (статично типізована)
10/10Відтворюване середовище — lockfile
10/10Підтверджена практика роботи з агентами — 77 з останніх 100 комітів створено агентом або з його зазначенням
0/8Автоматизоване супроводження — автоматичних оновлень залежностей не виявлено
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Використані вхідні дані
has_nixні
has_testsтак
lockfilesgo.sum
has_dockerfileні
typed_languageтак
bootstrap_files
has_devcontainerні
has_linter_configтак
typecheck_configs
agent_commit_share0,77
toolchain_manifestsgo.mod
dependency_bot_commit_share0
Як обчислюється оцінка
45/45Типізований код — Go (статично типізована)
55/55Керовані розміри файлів — 0/211 файлів вихідного коду понад 60 КБ
Використані вхідні дані
primary_languageGo
largest_source_bytes40 587
source_files_sampled211
oversized_source_files0
Як обчислюється оцінка
0/40Схема API (OpenAPI/GraphQL/proto)
0/20Сервер MCP
40/40Придатні до запуску приклади — example
Використані вхідні дані
example_dirsexample
has_mcp_signalні
api_schema_files

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

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

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

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

Докладніше

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

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

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests1 out of 1 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/1 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
0Licenselicense file not detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
н/дPackagingpackaging workflow not detected
10Pinned-Dependenciesall dependencies are pinned
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.
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
8Vulnerabilities2 existing vulnerabilities detected
Прямі залежності 14
РеєстрПакетОбмеження версіїМаніфест
Gocuelang.org/gov0.16.1go.mod
Gogithub.com/containerd/errdefsv1.0.0go.mod
Gogithub.com/google/go-containerregistryv0.21.5go.mod
Gogithub.com/hetznercloud/hcloud-go/v2v2.39.0go.mod
Gogithub.com/invopop/jsonschemav0.14.0go.mod
Gogithub.com/moby/moby/apiv1.54.2go.mod
Gogithub.com/moby/moby/clientv0.4.1go.mod
Gogithub.com/pkg/sftpv1.13.10go.mod
Gogithub.com/spf13/cobrav1.10.2go.mod
Gogo.uber.org/zapv1.27.1go.mod
Gogolang.org/x/cryptov0.54.0go.mod
Gosigs.k8s.io/kustomize/apiv0.21.1go.mod
Gosigs.k8s.io/kustomize/kyamlv0.21.1go.mod
Gosigs.k8s.io/yamlv1.6.0go.mod
Усі залежності не зібрано

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

Звіт у форматі JSON машиночитний
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 1166,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "Go": 1463925,
        "HCL": 10657,
        "Shell": 175057
      },
      "pushed_at": "2026-07-13T12:07:44Z",
      "created_at": "2026-04-22T04:40:24Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-13T12:08:05Z",
      "description": null,
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": null,
      "primary_language": "Go",
      "significant_languages": [
        "Go",
        "Shell"
      ]
    },
    "owner": {
      "blog": "https://www.yolean.com",
      "name": "Yolean",
      "type": "Organization",
      "login": "Yolean",
      "company": null,
      "location": null,
      "followers": 9,
      "avatar_url": "https://avatars.githubusercontent.com/u/11188184?v=4",
      "created_at": "2015-02-25T05:49:14Z",
      "is_verified": null,
      "public_repos": 99,
      "account_age_days": 4165
    },
    "license": {
      "state": "absent",
      "spdx_id": null,
      "raw_spdx": null,
      "file_present": false,
      "scorecard_found": false,
      "profile_has_license": false
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2026-07-13T12:07:45Z"
        },
        {
          "tag": "v0.4.7",
          "kind": "patch",
          "published_at": "2026-06-22T15:52:32Z"
        },
        {
          "tag": "v0.4.6",
          "kind": "patch",
          "published_at": "2026-05-13T17:47:09Z"
        },
        {
          "tag": "v0.4.5",
          "kind": "patch",
          "published_at": "2026-05-12T10:50:04Z"
        },
        {
          "tag": "v0.4.4",
          "kind": "patch",
          "published_at": "2026-05-12T08:17:08Z"
        },
        {
          "tag": "v0.4.3",
          "kind": "patch",
          "published_at": "2026-05-11T19:31:46Z"
        },
        {
          "tag": "v0.4.2",
          "kind": "patch",
          "published_at": "2026-05-11T08:09:44Z"
        },
        {
          "tag": "v0.4.1",
          "kind": "patch",
          "published_at": "2026-05-07T10:13:10Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2026-05-04T15:16:19Z"
        },
        {
          "tag": "v0.3.8",
          "kind": "patch",
          "published_at": "2026-05-04T13:40:23Z"
        },
        {
          "tag": "v0.3.7",
          "kind": "patch",
          "published_at": "2026-05-03T15:38:42Z"
        },
        {
          "tag": "v0.3.6",
          "kind": "patch",
          "published_at": "2026-05-02T05:22:49Z"
        },
        {
          "tag": "v0.3.5",
          "kind": "patch",
          "published_at": "2026-05-01T19:04:46Z"
        },
        {
          "tag": "v0.3.4",
          "kind": "patch",
          "published_at": "2026-04-30T15:37:00Z"
        },
        {
          "tag": "v0.3.3",
          "kind": "patch",
          "published_at": "2026-04-30T09:52:37Z"
        },
        {
          "tag": "v0.3.2",
          "kind": "patch",
          "published_at": "2026-04-29T05:23:35Z"
        },
        {
          "tag": "v0.3.1",
          "kind": "patch",
          "published_at": "2026-04-28T08:46:37Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-04-27T12:15:05Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-04-25T15:47:38Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-04-22T05:05:25Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "6e7b7132b6641f3a831f0fe134baa698a6017df5",
          "body": "Code review by Fable 5",
          "is_bot": false,
          "headline": "Merge pull request #30 from Yolean/integration-2026-07",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-07-13T12:07:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a097da147033adaba04fab1dc642a50d83e528c6",
          "body": "…ncy test\n\ndocker info runs under a 2s timeout that can occasionally time out\nunder CI load and succeed on the very next call, making two\nback-to-back DiscoverProvider() calls disagree even though the host\nhasn't changed. Cache the probe result for the process lifetime.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/config): memoize DiscoverProvider to fix flaky idempote…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-13T11:53:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "981942575e39ae77d65c29e0c00c53fbeb8e99ac",
          "body": "…ifetime-unified reaper)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'hetzner-provisioner-rebase' into integration-2026-07 (l…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:43:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5159f67484c3d59e45631ec7ae4f2fca3995eaa3",
          "body": "…diagnostics\n\nA pass-2 failure leaked the running VM (no t.Cleanup; the leaked\nVM then held the test's ports across reruns) and the sentinel\nFatalf discarded the command output that pinpoints whether the\nlabeled volume mounted at all.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(e2e/qemu): DataDisk test cleans up on failure and reports mount …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:42:36Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2bc71a4d28ab3ba5e3c87ef183829761074d1d6c",
          "body": "…signalling\n\nTeardownConfig went straight to stopVM's SIGTERM ladder, which is\na hard power-off: guest writes from the last few seconds (inside\nthe ext4 commit interval) never reached disk. That silently lost\nrecent data on the operator-preserved DataDisk -- the disk-reuse\ncontract's own teardown co\n[…]\ns guestPoweroff (sync + poweroff over\nssh, wait for qemu exit) before the signal ladder; unreachable\nguests fall through within the ssh timeout.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/qemu): teardown powers off the guest gracefully before …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:42:36Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "74a8d3df6c54d8ad4f3eac25537f15720184e55c",
          "body": "e2e/hetzner_test.go is fenced behind the hetzner build tag and was\ninvisible to lint. Adding the tag surfaced no findings, so this is\ncoverage only.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(lint): cover hetzner-tagged e2e files",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:33:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8bb9f2f0ee53d353d5dc38644717da816793ebdc",
          "body": "…s a fresh window\n\nLifecycle parity with the local lifetime semantics: a manual stop\nends this run's budget and a start re-anchors the full window at\ncluster start. Stop deletes the reaper Job while the cluster is\nstill reachable (best-effort with a logged warning; a frozen Job\non a stopped server i\n[…]\nat provision, so no\nYAML config is needed. A failed re-arm fails Start loudly since\nan un-armed reaper on a running server is unbounded billing.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): stop deletes the expiry reaper; start re-arm…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:33:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1fce2a56698d8c0b672b9d46c1927d44492e490b",
          "body": "…drop autoTeardownHours\n\nThe v0.4.7 lifetime knob (maxRun Go-duration budget + onExpiry\naction; empty disables) replaces the hetzner-specific\nautoTeardownHours. The in-cluster reaper Job is hetzner's expiry\nmechanism, the analog of GCP's max-run-duration, so the earlier\nlifetime rejection inverts in\n[…]\nhe readiness gate and the new expires-at\nannotation stay covered.\n\nautoTeardownHours never shipped beyond this branch so no migration\nis needed.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/config): hetzner adopts the standard lifetime config; …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:30:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6622f1cf3846b6ea21cae1ee2f4168b4c53f279f",
          "body": "…xpiry action\n\nThe reaper stops being teardown-only: installReaper takes a\nmaxRun window (seconds-resolution sleep) and an onExpiry action.\nteardown keeps the LB-first-then-server delete; stop is a graceful\nACPI shutdown that preserves the server identity. The Job carries\nmax-run / on-expiry / expir\n[…]\n\nProvision still drives this from autoTeardownHours with action\nteardown; the switch to the standard lifetime config follows in\nthe next commit.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor(provision/hetzner): reaper Job parameterized on duration + e…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:24:33Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "14b4bbd77dd8c103191b67e0a8798c55e12ffd4b",
          "body": "…ration-2026-07\n\nDescriptions are now comma-free at the source so the generated schema\ncarries the full text instead of truncating at the first comma.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(schema): regenerate hetzner.schema.json after rebase onto integ…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "15ba93bf0d7b15eeab20261c0215e19b87bf6000",
          "body": "The canonical spec lives in the separate specs repo\n(specs/y-cluster/HETZNER_PROVISIONER.md); this repo does not carry\nspec documents. The specs repo copy needs updating from this\nbranch's version, which was ahead of it (reaper reconciliation,\nphases 6-7). Code comments now point at the specs repo instead of\nan in-repo path.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(specs): drop in-repo HETZNER_PROVISIONER.md copy",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ee344227567820614a834c2dff3e15d58742ef29",
          "body": "Picks up the lifetime + gateway resources definitions that main's\nCommonConfig gained since the branch forked, plus the corrected\nautoTeardownHours and rejectUpstream descriptions. Keeps the CI\ngenerate-drift job green.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(schema): regenerate hetzner.schema.json after rebase onto main",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "dfaea50e04f3678988d035049e464032a31cf0f2",
          "body": "backoffLimit: 0 + restartPolicy: Never meant the first pod failure\n(node reboot, eviction, transient error) permanently killed\nauto-teardown, contradicting the survives-cluster-reboots design.\nbackoffLimit: 6 re-schedules the pod; every retry sleeps the full\nHOURS again so the teardown only ever mov\n[…]\n (never fires\nearly), and the delete steps carry already-gone guards so a retry\nafter partial cleanup is harmless. Manifest test pins the value.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/hetzner): reaper Job survives pod loss via backoffLimit",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "55758c90f4885eac122dda81c94f30ff2a877b6b",
          "body": "Three description/comment fixes and one validation rule:\n\n- rejectUpstream's jsonschema description contained a comma, which\n  is a field delimiter in jsonschema tags, so the generated schema\n  description was truncated to \"When true\". Reworded without the\n  comma.\n- autoTeardownHours' description s\n[…]\n hetzner and would silently\n  no-op otherwise. Unit test included; a disabled lifetime with only\n  the onExpiry tag default applied stays valid.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/config): hetzner schema descriptions + lifetime rejection",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8250798dfccbe95e09f96087b4b1365847a5ac7e",
          "body": "- pkg/images/s3.go: explicitly ignore the deferred minio object\n  Close error, matching the repo idiom for read-side closes\n- pkg/provision/hetzner/hetzner.go: labelSelectorForGroup now uses\n  the labelManagedBy const instead of duplicating the literal\n  (the const was otherwise unused)\n- pkg/provis\n[…]\nFprintf instead of\n  WriteString(Sprintf(...)) (QF1012)\n- preload_test.go, reject_upstream_test.go: De Morgan on negated\n  conjunctions (QF1001)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(lint): golangci-lint v2.11.4 findings in hetzner + images code",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fe507b55a448a29a07082e3a7cd3634108dc993b",
          "body": "…ing\" gap)\n\nBug: a pre-loaded image landed in containerd's k8s.io namespace\nunder the original push ref (e.g. \"hashicorp/http-echo:1.0.0\"),\nbut kubelet canonicalised that ref to \"docker.io/hashicorp/http-echo:1.0.0\"\nbefore the containerd lookup, found nothing, and pulled from\nupstream anyway. `kubec\n[…]\nHub registry).\n  - BuildPreloadScript_TagsCanonicalAliases asserts the script body\n    runs the right `ctr image tag` invocations.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(images): pre-load tags canonical Docker Hub aliases (closes \"Pull…",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6e490ebd2ac8c7cbb67a68878cf6931177fe7c98",
          "body": "Two new subcommands under the existing `images` group:\n\n  y-cluster images remote list   # ref / digest / blob-count per image\n  y-cluster images remote stats  # bucket-wide dedup + storage report\n\nThe list output is a TSV-shape table (works with `column -t` / awk):\n\n  REF                           \n[…]\nzes; the manifest line will always\ntotal ~KB (oci-layout + index.json), making any non-trivial\noci/ bytes recognisable as orphans.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(images): images remote list/stats operator-facing cache report",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2647e039aae105387e5fd86588e351d61f56e821",
          "body": "Closes the \"I have a provisioned Hetzner cluster but the LB IP is\nsilent\" gap. Two new pieces:\n\n1. Per-cluster default Gateway (pkg/provision/hetzner/default_gateway.go)\n\n   Provision now applies a Gateway resource in y-cluster-gateway\n   namespace after envoy-gateway's controller is up. Without thi\n[…]\nssions. Hostname has to match the HTTPRoute's hostname\nexactly because envoy-gateway routes by Host header, not by\ndestination IP.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): default Gateway + public-safe example workload",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9aa2d8172b01b29451f833d4cb9255766fed5f76",
          "body": "Two images sharing a layer now share storage and upload bandwidth.\n\nS3 layout v2: blobs hoisted from the per-image OCI prefix to a\nbucket-level content-addressed prefix.\n\n  Before (v1):\n    s3://<bucket>/oci/<safe-ref>/<digest>/blobs/sha256/<hex>\n    s3://<bucket>/oci/<safe-ref>/<digest>/index.json\n\n[…]\nd: bucket-with-many-entries makes\n    Provision slower (197s vs 112s on a 6-image bucket vs 1).\n    Operator decides what to push.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(images): layer dedup via content-addressed shared blobs (phase 7)",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "136e72da70ce41cf9e8bd2929a79e2209dd35e96",
          "body": "…chanism\n\nThe original design said \"drop registries.yaml with wildcard\nmirror to empty endpoint set\". E2E proved that's insufficient:\nk3s v1.35.4 falls back to the original registry despite the\nwildcard. Spec now documents what shipped:\n\n  - hosts.toml direct under certs.d/<reg>/ -- containerd treat\n[…]\nt's first pull of hetznercloud/cli.\n\nFuture agent sessions should land on the shipped mechanism, not\nthe original (broken) design.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(provision/hetzner): record rejectUpstream's hosts.toml-direct me…",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "285eb032d987dd4b899486756a8a6c3e7ca45012",
          "body": "….toml (phase 6.d)\n\nWhen HetznerConfig.ImageCache.RejectUpstream is true, the last\nstep of Provision drops:\n\n  1. /etc/rancher/k3s/registries.yaml -- k3s source-of-truth, so a\n     future `k3s server restart` regenerates certs.d the same way.\n  2. /var/lib/rancher/k3s/agent/etc/containerd/certs.d/<r\n[…]\now up.\n  - The reaper-readiness gate adds wall time to Provision but\n    is necessary -- a stuck reaper costs more than 7 seconds.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): rejectUpstream lockdown via containerd hosts…",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bd448cbfeb5b7c9739be74c2a648dbec5ddee058",
          "body": "When HetznerConfig.ImageCache.Bucket is set, Provision pulls every\nentry from s3://<bucket>/index.json into the node's containerd\nbetween MergeKubeconfig and the LB/cert/envoy-gateway steps.\nLands here so envoy-gateway's pull also benefits from cache hits.\n\nOperator side (pkg/images/preload.go):\n\n  \n[…]\n failed run grants read access to one cached image for the\n    same 24h. Acceptable for a dev cache shared inside one\n    project.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): pre-load images from S3 cache (phase 6.c)",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a388022708c139342fd429fade945e999f479425",
          "body": "`y-cluster images push <ref>` resolves a tag (or digest), runs the\nexisting pkg/images.Cache to materialise the OCI v1 image layout\nlocally, then mirrors the layout to s3://<bucket>/oci/<safe-ref>/<digest>/\nand merges an entry into s3://<bucket>/index.json.\n\nSmoke-tested against the live y-cluster-e\n[…]\nver AWS\ncredentials from a Hetzner-only project). Binary delta on a\nrelease build is +382KB; vs aws-sdk-go-v2 +147KB. Trade taken.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(images): push OCI layouts to Hetzner Object Storage (phase 6.b)",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c9d49933a272f980667476122d4737fbcc01e13c",
          "body": "…6.a)\n\nLays the config surface for the S3-backed image cache without\ntouching behavior. Bucket empty = cache disabled (the current\ndefault); a non-empty bucket flips the feature on and gets\nregional defaults (Region=hel1, IndexKey=index.json).\n\nValidation guards three failure modes the operator can \n[…]\nea. Description text still documents the\nruntime defaults for schema readers.\n\nJSON Schema regenerated via cmd/internal/schemagen.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/config): hetzner imageCache field + validation (phase …",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "04d9d2c09bf4cda9631a2be1cad5fcb026c31d58",
          "body": "Locks the design before the implementation lands so the operator\nsees the trade-offs in writing.\n\nThree opt-in deliverables under one HetznerConfig.imageCache field:\n\n  - operator-side `y-cluster images push <ref>` uploads an OCI\n    layout to s3://<bucket>/oci/<safe-ref>/<digest>/, mirroring\n    th\n[…]\nter CAS-via-etag pass).\n  - Layer dedup deferred to a follow-up phase if measurements\n    show large overlap across cached images.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(provision/hetzner): design image cache via Hetzner S3 (phase 6)",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c903ce232d99043072d467035d2d96af069e9263",
          "body": "…are-export, pause/resume)\n\nCloses the parity gap the spec end-goal #2 calls out.\n\n  - stop: hcloud Server.Shutdown via hetzner.Stop. Graceful ACPI;\n    operator gets the cluster back via `start` without a re-provision.\n    Billing continues for the disk while stopped (Hetzner has no\n    \"frozen, no\n[…]\nectation that it's coming.\n\nAdds hetzner.HasState (sidecar-presence probe) for the\nprepare-export and start branches; unit-tested.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): lifecycle dispatch parity (stop, start, prep…",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8652ef7ce0c8449a12f437091414d965b3f6ef5e",
          "body": "…shipped reaper\n\nThe spec referenced from TMP_HANDOVER.txt as the \"first read\" did not exist on\ndisk; check it in so future agent sessions actually land on a Phases table that\nmatches the code.\n\nSpec deltas vs. the original draft (recorded so future readers don't get\nmisled by the locked Decisions t\n[…]\nJob\".\n  - pkg/provision/hetzner/hetzner.go package-doc phase list: phase 2 is\n    in-cluster reaper, phase 4 is positional source.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(provision/hetzner): land HETZNER_PROVISIONER.md, reconcile with …",
          "author_name": "Yolean macbot01",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "07f2c4f6dbfbd691233f9765b459445761e7e513",
          "body": "Replaces the \"auto-teardown not implemented; run teardown\nyourself\" Warn with a real cluster-side reaper that survives the\noperator's machine going away. The trigger lives ON the cluster\n(an embedded Kubernetes Job), so a wiped/retired/quit-employee\nlaptop no longer strands the paid Hetzner resource\n[…]\nter reaper completes,\nthe operator's interactive y-cluster teardown sweeps the\norphaned cert + SSH key, leaving the project empty.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): in-cluster auto-teardown reaper Job (phase 2)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bad20ef1046b8c869a947032bc8be8c58db439c7",
          "body": "…hase 3.c.2)\n\nWires the cert generator from 087f35d into the LB lifecycle so\nclients dialing the LB on 443 get a real TLS handshake against\na per-context self-signed cert, with SNI selecting the right cert\nwhen multiple contexts share an lb-group LB.\n\nLB service shape changed:\n\n  - 80: TCP -> 80 (kl\n[…]\n). That can't happen under the\nnormal Provision-always-adds-a-cert flow but tolerates an\noperator who manually mucked with the LB.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): LB-side TLS termination via uploaded cert (p…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d8a75e4baf1247e4d954dca800ea9b3ecece2cb7",
          "body": "Pure crypto/x509 helper that produces a PEM-encoded self-signed\ncert + RSA private key for the per-context TLS termination wired\nin the next commit. No Hetzner API calls, no LB integration --\njust the byte-shape Hetzner Certificate.Create needs.\n\nDecisions worth keeping:\n\n  - Self-signed leaf, no se\n[…]\nIP regression\nguards against the gotcha of net.ParseIP returning a nil net.IP\nthat crypto/x509 would still accept and crash later.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): self-signed cert generator (phase 3.c.1)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b376c8f2d2a2919635541e3288d324fd9bfef978",
          "body": "Hetzner's create-load-balancer-with-services API rejects a TCP\nhealth_check that omits the port field (responds with a generic\n\"invalid input in field 'services'\"); the CLI adds the\ndestination_port as default, but the API itself does not.\n\nTwo issues fixed:\n\n  1. health_check.Port now set explicitl\n[…]\no containerd, teardown -> empty project. Both LB services\nreport health_check.port == listen_port == destination_port\nas expected.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/hetzner): explicit per-service LB health-check port",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e194d29e506cbddb31313e2c65652bc1b9c0cf63",
          "body": "Phase 2 attempted host-side at(1) scheduling for auto-teardown.\nRejected by Staffan on the architectural ground that a laptop\nwhich gets wiped, retired, or simply belongs to someone who quit\nleaves the Hetzner server stranded forever -- the exact \"don't\nleak\" failure mode auto-teardown is supposed t\n[…]\nzner/auto_teardown_test.go\n  - state.AtJobID field\n  - the scheduleAutoTeardown / atRemove call sites in\n    Provision / Teardown.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "revert(provision/hetzner): drop at(1) auto-teardown; pin to follow-up",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ac040112fdad29e0cb33d7a4e871adf8e58dbcaf",
          "body": "Wires the hetzner provisioner into the cluster.Lookup detection\nchain so \\`y-cluster ctr\\`, \\`y-cluster crictl\\`, \\`y-cluster\nmanifests add\\`, and \\`y-cluster images load\\` all work against a\nHetzner-provisioned cluster the same way they work against qemu /\ndocker / multipass.\n\nDetection: <cache-dir\n[…]\nerRunning_MissingOrInvalid pins the\n    fall-through-on-bad-state behaviour so Lookup doesn't\n    produce a half-populated result.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(cluster): hetzner backend in Lookup; testdata example (phase 5)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1f28f68b274ae703f829eddd771852e8e676d70c",
          "body": "\\`y-cluster images load <url>\\` now accepts an http(s):// URL as\ninput source, in addition to a file path or \"-\". The byte stream\nis the contract; the helper just opens an HTTP GET and pipes the\nresponse body into the same downstream code path that file and\nstdin use. The motivating use case is the \n[…]\ntx deadline so\n    a TEST-NET-1 connect attempt fails fast.\n  - TestOpenInput_FileBranch / _StdinBranch as regression\n    pinning.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(cmd/y-cluster): http(s) URL input for images load (phase 4)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4fb52e068ac3ebe809c1e7e745eedd8164c58bac",
          "body": "…e 3.b)\n\nAfter ensureLoadBalancer, Provision now installs envoy-gateway via\nthe existing pkg/provision/envoygateway helper and stamps the LB's\npublic IPv4 onto the y-cluster GatewayClass under\nyolean.se/dns-hint-ip. ystack's y-k8s-ingress-hosts walks the\nGatewayClass annotation to populate /etc/host\n[…]\nothing on the node.\n\nPhase 3.c will land cert-manager + Listener TLS so the LB's 443\nservice has a real HTTPS responder behind it.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): envoy-gateway + dns-hint-ip on LB IPv4 (phas…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b601ab4ca4672edacfe744b801cb113e76a9cd98",
          "body": "Provision now ensures a Hetzner LoadBalancer for cfg.LBGroup\n(default = \\$USER) and includes the new server. Teardown deletes\nthe LB iff this server was the last lb-group member, so multi-\nmachine devs share one LB and pay for it only while at least one\ncluster is up.\n\nArchitecture:\n\n  - LB name: y-\n[…]\nrMatchesServerCreate pin\nthe name shape and the invariant that the LB's label_selector\nresolves the same servers Provision tagged.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): shared lb-group LB attach (phase 3.a)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1b990455d98206b6080f4c3a8ea33ea2abb15ae8",
          "body": "Schedules a host-side at(1) job at the end of Provision that runs\n\\`y-cluster teardown -c <Dir>\\` after cfg.AutoTeardownHours hours\n(default 8). The job id is captured in the state sidecar so\nTeardown can atrm it cleanly; if the operator misses the window,\natd runs the missed job at next boot, which\n[…]\nuns only when HCLOUD_TOKEN is\nexported) -- a cheaper unit test would mock exec.Command, which\nis overkill for a four-call wrapper.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): at(1) auto-teardown (phase 2)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "361adfc5d289f8a9799d15b2ef7f376c4eb753c8",
          "body": "Wires the install half of phase 1 onto the bare Provision/Teardown\nlanded in 2a8df37:\n\n  - installK3s runs the canonical curl|sh installer over SSH after\n    waitForSSH succeeds. Flags: --tls-san=<ipv4> +\n    --node-external-ip=<ipv4> so the kubeconfig the operator pulls\n    back is dialable, plus t\n[…]\ne envoy-gateway\n    rollout to the shared LB in phase 3; doing it here would mean\n    a half-functional gateway pointing at no LB.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): k3s install + kubeconfig merge (phase 1.b)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b5d13338f81f2617525325ae6366a5f31ef06a4b",
          "body": "The hetzner provisioner now creates and tears down Hetzner Cloud\nservers. NO k3s install or LoadBalancer yet -- this commit lands\nthe lifecycle bones plus the SSH wiring so phase 1.b can do the\nin-server install over the same SSH channel cluster.Lookup will\nlater use for ctr / crictl / RunShell.\n\nWh\n[…]\nuse Hetzner's image ships with a\n    `preserve_hostname: true` default that ignores cloud-init's\n    hostname directive otherwise.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/hetzner): bare Provision/Teardown (phase 1)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "412f9997917807dde0e72300adea7fd2f3e25bbc",
          "body": "First step of the Hetzner provisioner branch: types, validation\nrules, and dispatch. No Hetzner Cloud API calls land yet -- this\njust lets the build know that \"hetzner\" is a real provider value\nand rejects malformed configs at load time.\n\nWhat lands\n\n  - ProviderHetzner const and AllProviders entry.\n[…]\nfety\n    belt). Settled on \"0/unset = use default, mandatory\". A truly\n    long-running dev cluster needs an explicit hours value.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(provision/config): hetzner provider scaffolding (phase 0)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T11:13:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "03fff8e55c13dc34d4e8233931965ce18b37dbd2",
          "body": "…rmin kernel-selection guard)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'prepare-export-contract' into integration-2026-07 (supe…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:38:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "be19ec37cdfa6516c9915c3c5e76b24138094dcf",
          "body": "…election\n\nsupermin builds its appliance from the NEWEST /boot/vmlinuz-* that\nhas a matching /lib/modules/<release> dir, not from the running\nkernel. The guard checked only the running kernel, so a host with\na newer 0600 kernel installed but not yet booted (the normal state\nafter an unattended kerne\n[…]\non (numeric release ordering, module-dir\ngate, running-kernel fallback) in both the binary and the sourced\nscripts/_check-host-kernel.sh helper.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(libguestfs): kernel-readability guard mirrors supermin's kernel s…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:38:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "95e6c10224c9fc664abe0e9f03e80d0eca521b35",
          "body": "…ed-disk instance-id, lint coverage, schema descriptions)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'prepare-export-contract' into integration-2026-07 (stag…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:25:31Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b46d25b955ec198524e68a2536f1397328262d2e",
          "body": "Commas are field delimiters in jsonschema struct tags, so every\ndescription containing one shipped truncated in the generated\nschemas: gateway.skip rendered as just \"If true\", the resource\nrequest docs cut at \"Default cpu 10m\", the notation examples cut\nat \"(e.g. 10m\", and the multipass image / localstorage\npathPattern docs lost their tails. Reworded without commas and\nregenerated.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/config): comma-free jsonschema descriptions",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:23:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8ae2d6d3a56d31e5e0de2e450cf1a0918b8abcca",
          "body": "golangci-lint ran without build tags locally and in CI, so every\nfile behind the e2e/docker/kvm/multipass tags was never linted.\nAdd the tags to .golangci.yaml and fix what surfaced: two\nerrcheck misses (net.Listener Close in the registry helper,\nbest-effort kubectl delete in the yconverge e2e) and one QF1001.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(lint): cover tag-fenced e2e files, clear the debt that hid there",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:22:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d2e7667709f2c1cfbfa4ad48c8194dc85d53a803",
          "body": "…vision can SSH\n\nProvision always generates a fresh SSH keypair (ensureSSHKey\ndeletes and regenerates) and relies on cloud-init to install the\npublic half. cloud-localds' default meta-data hardcodes\ninstance-id iid-local01, and cloud-init only re-applies user-data\nwhen the instance-id changes, so bo\n[…]\nage, so\nthe id changes only across provisions, where the keypair changes\nanyway. The meta-data file joins the per-VM artefact list for\nteardown.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/qemu): unique cloud-init instance-id so staged-disk pro…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T08:22:02Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ab1ec7502f0b7c858777b5c038725c75980eb00a",
          "body": "…contract alignment)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'prepare-export-contract' into integration-2026-07 (e2e …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:58:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3f0e465ea03de1eced7b9986a5b7a185bbdcb950",
          "body": "…ract\n\nThree kvm-gated tests still ran the retired stop-then-prepare\nsequence, which PrepareExport has rejected since it gained the\nlive phase (it needs the apiserver up and stops the VM itself).\nCI never runs the kvm leg, so v0.4.7 shipped with\nTestQemu_PrepareExport_GracefulShutdown, TestQemu_Seed\n[…]\nnged: the\nSIGTERM window under test is prepare-export's internal stop,\nwhich is the same lifecycle.Stop path the tests called\nexplicitly before.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(e2e/qemu): align prepare-export callers with the live-phase cont…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:58:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "cfe2f18a6d402441860296fc90600b445b1cf288",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'k3s-1.35.6' into integration-2026-07",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:11:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ac75eba0f284870dd13af78b1e31483364d4b941",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'envoy-gateway-1.7.5' into integration-2026-07",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:08:17Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3014fca21365ab316756174536f10f0787ac0ae3",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'prepare-export-contract' into integration-2026-07",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:08:17Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7ffdfd9da9d01f87e42f9595e3517704d9f46235",
          "body": "Two patch releases on the supported v1.35 channel, carrying the\nupstream Kubernetes security fixes. Schemas regenerated per the\npin file's bumping flow.\n\nThe ghcr mirror tag does not exist until mirror-k3s.yaml runs on\nthe path change after this merges to Yolean main (or is\nworkflow-dispatched there); until then provisions resolve the\nimage via the documented docker.ResolveImage fallback to\ndocker.io/rancher/k3s with a warning.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build(k3s): bump pin v1.35.4+k3s1 -> v1.35.6+k3s1",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:03:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fae52abb6e68e9f72ed48b7c6ea43e9eba241a1e",
          "body": "v1.7.5 (2026-07-08) bumps the bundled Envoy proxy to 1.37.5 which\nincludes multiple upstream security fixes, and the v1.7.3/v1.7.4\npatches in between carried an auth fail-open fix. Same minor\nline, so the CRD/controller surface is unchanged.\n\nThe package doc had drifted badly enough to mislead exact\n[…]\ndownloads install.yaml per version into the\ncache on first use and renders the GatewayClass in Go. Rewritten\nto describe the mechanism as built.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build(envoygateway): bump Envoy Gateway pin v1.7.2 -> v1.7.5",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T07:00:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ca6783d3c91f4adf0effa498e6433057e05911d0",
          "body": "Implements option 1 of the specs repo's\nISSUE_QEMU_PROVISION_SSH_TIMEOUT.md: cold-cache first boots spend\n5-15 minutes in cloud-init and the 120s abort left qemu running,\nso the next provision hit \"VM already running (pid N). Teardown\nfirst\". Heartbeat lines every 30s and a console-log pointer in\nboth the initial log line and the timeout error replace the\nsilent wait.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/qemu): 600s SSH wait with heartbeat, was 120s",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:57:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8583a30b646f5713260c2a3be4a90dd1e135262e",
          "body": "…e no Gateway API\n\nThe live phase hardcoded GatewayClass \"y-cluster\" even though\ngateway.className is configurable, and the configured name is not\nrecoverable from the qemu state sidecar. A deploy on a renamed\nclass failed prepare-export outright (ClearDNSHintIPAnnotation\nerrors on a missing class d\n[…]\nn stays for the\n  explicit --gateway-class CLI path, with its comment corrected:\n  a missing class fails loudly there, which suits a typed name.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(prepare-export): clear dns-hint-ip on all GatewayClasses, tolerat…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:56:42Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "5c51301c7cb725e0e5c3c1b1850fd027f673e9bd",
          "body": "The stop-first sequence in the prepare-export help text, its file\ncomment and APPLIANCE_MAINTENANCE.md predated the live phase: the\nimplementation errors unless the VM is running (it needs the\napiserver to clear dns-hint-ip and snapshot gateway state) and\nstops the VM itself before the offline phase\n[…]\no\nvirt-customize, the stale qemu-only claim on the lifecycle comment\n(stop dispatches to all three backends) and the gateway command\nchild list.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: prepare-export contract is provision then prepare-export",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:54:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b1c188c1b923c893e796d7835af647dc322ee5cf",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'security-toolchain-bumps' into integration-2026-07",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:47:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a4473fc149423eb3f40f4486883f1c985aa5c00c",
          "body": "Integration work branch: collects CI-green unmerged increments for\nreview. Upstream PRs will be replayed from the individual branches.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Merge branch 'appliance-workflows-rebase' into integration-2026-07",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:47:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a387d1f0f7a406440e69dca60d0b41464e5e5b4b",
          "body": "govulncheck reports 18 vulnerabilities in called code at the previous\npins, all cleared by these three bumps:\n\n- Go 1.26.1 -> 1.26.5: 11 reachable stdlib fixes, including the\n  crypto/x509 name-constraints bypass (GO-2026-4866) and net/http\n  fixes. CI reads go-version-file so lint/test/build/releas\n[…]\nix GO-2026-5026.\n\nx/sync, x/sys, x/text move as transitive requirements of the above.\nUnit tests and golangci-lint v2.11.4 pass at the new pins.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build(deps): Go 1.26.5, x/crypto v0.54.0, x/net v0.57.0",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:14:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "978086d643fd0d5ba2aacd3044f025e40fc73781",
          "body": "The probe window message did arithmetic on ATTEMPTS which was never\nassigned (the probe() local is lowercase attempts), so the failure\npath reported \"never answered within 0s\". Name the shared default\nPROBE_ATTEMPTS and use it in both places.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(scripts/e2e-hetzner): failure diagnostics referenced unset ATTEMPTS",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-07-10T06:07:46Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "809b948482da5c4c7be6335833271ebc94655fff",
          "body": "…ernel helper\n\nNow that v0.4.7 (PR #29) shipped the lifetime feature, the libguestfs\nhost-kernel check, and scripts/_check-host-kernel.sh to main, wire the\nappliance scripts -- rebased on top of that release -- to actually use\nthem:\n\n- All four appliance build / e2e scripts now source\n  scripts/_che\n[…]\nd). The\n  GCP e2e sets a lifetime and asserts the instance's scheduling shows\n  maxRunDuration + instanceTerminationAction=DELETE.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts): integrate appliance scripts with released lifetime + k…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:04:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "95c89e8a17c723c4e949156da427496198f924e5",
          "body": "Both files-pointed-at-by-env-var inputs surfaced the same\nfoot-gun: a malformed value passed the existence check but\nfailed deep inside the tool we shelled into, with a less\nhelpful message:\n\n  - GCP_KEY pointing at a truncated / wrong-format JSON\n    (e.g. a re-exported key that lost its private_ke\n[…]\n  it at config-load time with a message naming the valid\n    values.\n\nBoth checks fire BEFORE any cloud-side state change. Adds no\nnew dependency: jq is already required by the broader\nappliance flow.",
          "is_bot": false,
          "headline": "feat(scripts): fail-fast schema checks on GCP_KEY and H_S3_REGION",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c2eabc70a5ceaf2b4d5829f31d05cd462acff179",
          "body": "The previous commit added \"e2-medium's\" and \"there's\" inside the\nsingle-quoted YHELP heredoc. Single quotes in bash can't contain\nsingle quotes, so the apostrophes terminated the string mid-block;\nthe resumed unquoted \"4 GiB OOMs ...\" got parsed as a command,\nand any consumer that sourced or executed the help block saw\n\"line 76: 4: command not found\".\n\nReworded to avoid the apostrophes entirely. bash -n parses the\nfile clean and --help renders the section as intended.",
          "is_bot": false,
          "headline": "fix(scripts/qemu-to-gcp): drop apostrophes from YHELP block",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f3535e6dd6eadc636f649785948ddb7d6c4c32b1",
          "body": "…each build)",
          "is_bot": false,
          "headline": "GCP NEG endpoint re-attach is idempotent on re-runs (VM is recreated …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1875afc46d8e9323b8377e97d906b7cc61d560c6",
          "body": "…e stack)\n\nObserved an appliance build that ran fine for ~2h at 91-93% memory\non a 4 GiB e2-medium, then died at 100% CPU / 3807 MiB used: ssh\nbanner exchange timed out, :443 + :6443 went REFUSED while :80\nkept LISTEN with the userspace too starved to respond. Classic OOM\nspiral. The full appliance \n[…]\nlp\ntext and the default-assignment comment so the next operator\nreading either spot sees why we don't surface a GCP_MEMORY knob.\nGCP_MACHINE_TYPE stays as the escape hatch for highmem / larger\nshapes.",
          "is_bot": false,
          "headline": "fix(scripts/qemu-to-gcp): default to e2-standard-2 (e2-medium OOMs th…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "70d65711a025103898c57f912000469182b0b3b3",
          "body": "…hooks\n\nThe unattended flow had ASSUME_YES + TLS_DOMAINS=auto landed\nalready, but no work-doing hook in PROMPT 1's hands-on window.\nResult: a build with ASSUME_YES=1 reached prepare-export with\nonly the y-cluster echo HTTPRoute applied; TLS_DOMAINS=auto\nthen aborted because the cluster had no non-wi\n[…]\nTE_VM_IP, etc.) are exported as empty strings.\n\nNon-zero exit aborts under set -e. Local cluster / VM / LB\nstay up for inspection.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts/qemu-to-gcp): APPLIANCE_SEED_CMD + APPLIANCE_VERIFY_CMD …",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "950cab27f8115d4b3c65c93f0ceb637ade5a3668",
          "body": "Drops the parallel-list footgun: today the operator declares\nhostnames in HTTPRoute manifests AND in TLS_DOMAINS, and drift\nbetween the two means the LB cert covers hostnames the cluster\ndoesn't serve, or vice versa.\n\nSetting TLS_DOMAINS=auto now resolves the FQDN list by calling\n`y-cluster gateway \n[…]\nefore.\n\nEmpty result aborts with an explicit error (operator asked for\nauto, none found = something wrong with the cluster state).\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts/qemu-to-gcp): TLS_DOMAINS=auto derives from gateway state",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8e60515a51bf99ef6cc89379ebf4ba127d5744cd",
          "body": "PR #20 changed prepare-export to require the cluster RUNNING:\nits live phase clears the per-deploy dns-hint-ip annotation\nand snapshots reconciled Gateway state into <cacheDir>/<name>-\ngateway-state.json (both need the apiserver up). prepare-export\nthen stops the VM itself before its offline (virt-c\n[…]\nst the stopped VM.\n\nDrop the explicit stop call. Update the docstring stage list\nto reflect that prepare-export does its own stop.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(scripts/qemu-to-gcp): drop explicit stop before prepare-export",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3b37bf345fc09efc415e9d6da8038cfa5a051540",
          "body": "The build VM occasionally OOMs during heavier customer\nworkloads applied at PROMPT 1 (mariadb + kafka + envoy +\nthe bundled controllers all in 4GB is tight). 8GB matches\nthe y-cluster default for stand-alone provisions but the\nqemu-to-gcp script was overriding it down to 4GB to keep\nthe host's headr\n[…]\ng.QEMUConfig.applyDefaults), so other provisioner\nflows (multipass, docker, plain qemu) are not affected.\nDisk size stays at 40GB.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(scripts/qemu-to-gcp): bump build VM memory 4096 -> 8192",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c45b8e98bfe04fd79f5f341394bf412452e09d64",
          "body": "…own-side PRESERVED message\n\nState preservation across appliance redeploys is the overarching\ndesign goal of the data-seed mechanism (commit f69addf +\nAPPLIANCE_MAINTENANCE.md). What was missing on the operator-\nfacing side: the QA-flow build script silently reused the\npersistent disk on every redep\n[…]\nhe revert of the route-\nenumeration block that this same teardown-issue debugging\nsurfaced as post-import SSH+kubectl scope-creep.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts/qemu-to-gcp): explicit --reuse-disk / --keep-disk; teard…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ab1f5c71dc22165a6fe31852d70c32111240cebe",
          "body": "Two related fixes for the GCP appliance smoke flow:\n\n1. do_tls_frontend now creates a :80 forwarding rule that 301s\n   to :443. Previously the function set up only a :443 listener,\n   so any `curl http://<lb-ip>/...` against the LB IP hung at TCP\n   connect (no listener on 80). Hangs from `curl ... \n[…]\ninst ext-app01-* in this commit (would require\nmutating an in-use LB the operator owns); it lands on the next\ndo_tls_frontend run.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts/qemu-to-gcp): :80 -> :443 LB redirect + Gateway-route probe",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7ce65b73d9e4ec91457c1625e893831359bae5c9",
          "body": "Adds a post-deploy step that offers to stand up a GCP regional\nExternal Application Load Balancer in front of the appliance VM\nwith a self-signed cert covering operator-supplied FQDNs.\nIdempotent (describe-then-create) so re-runs converge; teardown\nintegrated into the existing teardown subcommand.\n\n\n[…]\nsing resources are not\nerrors. The `Will DELETE:` inventory now lists `${NAME}-tls-*`\nwhen a forwarding rule of that shape exists.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts/qemu-to-gcp): optional regional HTTPS LB at end-of-build",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ed68ea61e65bbb678f63260ac711281510a1aaa0",
          "body": "Symmetric with APP_HTTP_PORT / APP_API_PORT: a new\nAPP_HTTPS_PORT env var lets operators override the host port\nforwarded to guest 443. Unset means \"let y-cluster apply its\ndefault\" -- the YAML still omits the field when no port var is\nset, which matches the behaviour for the other ports.\n\nWithout t\n[…]\nmodern Linux distros allows binding 443 without root the same\nway 80 already does.\n\nYHELP entries updated to surface the new knob.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(scripts): also forward host:guest 443 conditionally",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4df691388a06831ce1ad7117a095d21aedf454b3",
          "body": "The appliance-build / e2e scripts each carried a defaults block:\n\n    APP_HTTP_PORT=\"${APP_HTTP_PORT:-80}\"\n    APP_API_PORT=\"${APP_API_PORT:-39443}\"\n    APP_SSH_PORT=\"${APP_SSH_PORT:-2229}\"\n\nthen interpolated those into the heredoc'd y-cluster-provision.yaml.\nThree of the four values restate y-clust\n[…]\nft as-is (test-only; the import-side qemu is started directly,\nno y-cluster CLI involvement, so y-cluster's defaults don't\napply).\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(scripts): drop port defaults from bash, defer to y-cluster's",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "5a3c67d4db709ef9d74481420aa87e3a70adc585",
          "body": "Appliance e2e / build flows install workloads, build a seed\ntarball, prepare-export, and re-boot from the prepared disk -- the\ncumulative footprint pushes the 20G default disk into pressure on\nthe kubelet's image-gc thresholds, which surfaces as flaky pod\nevictions mid-test or mid-build.\n\nBump to 40\n[…]\ns with actual\nusage; the larger virtual size is a no-cost ceiling. The GCE side\nsimilarly uses a thin-provisioned persistent disk.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: bump appliance VM/disk size to 40G (avoid disk-pressure flakes)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "006ae88ebf0861cfecc69866a7d61dac918c57e1",
          "body": "Yolean dev / setup scripts that smoke-test the gateway expect a\nhost-side port that reaches guest 80. Today's qemu-side host port\nforwards default to 39080 in both the Go e2e helper and the bash\nappliance-build scripts, so any consumer that hardcodes\nhttp://localhost:80 has to remember the offset.\n\n\n[…]\nurl examples updated to match. Override via env\n    (APP_HTTP_PORT=39080 ./scripts/...) on hosts that keep port 80\n    privileged.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: align local gateway access on port 80 (e2e + scripts)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8f9c276b61619321f786203281f9bc71cb899501",
          "body": "Brings every scripts/ change from agents/appliance-export-import\nto upstream main as a single bump. The Go and testdata side of\nthis work landed in #19 (appliance-primitives); this commit is\nthe operator-facing bash that drives the released binary\nthrough the appliance lifecycle, plus the .env-style\n[…]\n #19. Both `go build ./...` and `go test\n./...` are unchanged-clean on this branch -- the scripts add\nno go.mod or testdata edits.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(scripts): appliance build / publish / e2e drivers",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T16:01:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6af8cc4357ab0e3f77c5fe2d90554b039f3732d9",
          "body": "Teardown automation",
          "is_bot": false,
          "headline": "Merge pull request #29 from Yolean/teardown-automation",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-06-22T15:51:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d8f764bb43d5e5610b06e2cf215a388259622596",
          "body": "Surfaced when PR #29 rebased this work onto main and CI ran on it for\nthe first time.\n\nLint (staticcheck):\n- ST1005 (pkg/provision/qemu/libguestfs.go): the remediation error\n  string ended with a period. Drop it; the multi-line body is unchanged\n  (only a trailing newline/punctuation trips ST1005).\n\n[…]\nsources it yet: the appliance workflow branch (which carries the\nappliance-*.sh scripts that source it) will be rebased onto this.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): lint + the GHA failure with the libguestfs setup helper",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T15:46:54Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bfb0fb60f0eb03e51672755b12987f47fb3139cb",
          "body": "libguestfs (virt-customize / virt-sysprep / virt-tar-out / virt-format)\nbuilds a supermin appliance from /boot/vmlinuz-$(uname -r). Ubuntu ships\nthose images mode 0600, so after every kernel upgrade a fresh root-only\nimage lands and the tools fail with the opaque \"supermin exited with\nerror status 1\n[…]\nifies the host. The remedy is\na single copy-pasteable block the user runs once with sudo; the hook\nthen covers all future kernels.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(libguestfs): one durable remedy for unreadable host kernel",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T08:57:13Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b7b3cde8f0bf4981ae35685c22b970110e95b7f3",
          "body": "A dev cluster left running after a task is paused or finished is pure\ncost. A lifetime.maxRun budget makes the cluster expire on its own,\ntaking the cheapest environment-appropriate action.\n\nModel:\n- Trigger is an absolute TTL (maxRun, a Go duration), counted from\n  when the cluster STARTS, re-ancho\n[…]\nlifecycle surface; docker/multipass keep the \"not yet implemented\"\nshape. Idle-based expiry and an in-cluster reaper are deferred.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(lifetime): cost-control auto-expiry for clusters",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-06-22T08:55:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c370b31cba8bdfe0b35176e623729bb670165a77",
          "body": "Primitives for qemu data disk reuse",
          "is_bot": false,
          "headline": "Merge pull request #28 from Yolean/qemu-data-disk-reuse",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-13T17:46:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "de6c8de75e14c404efa718805992fdb28aa40acb",
          "body": "Pre-FR 4 the import -> boot path was a deadlock:\n\n  - `y-cluster import <disk>` writes <CacheDir>/<Name>.qcow2\n  - `y-cluster provision` errors: \"disk already exists; run\n    teardown or start\"\n  - `y-cluster start` errors: \"kubeconfig context X has no\n    associated cluster; nothing to start\" -- th\n[…]\n to.\n\n  TestQemu_ExportImport (the VMDK-flavoured counterpart) takes\n  the same code path; same FR 4 fix unblocks it.\n\nRefs:\n  - specs/y-cluster/FEATURE_REQUEST_DISK_REUSE_E2E_PRIMITIVES.md\n    (FR 4)",
          "is_bot": false,
          "headline": "feat(provision/qemu): provision uses staged disk from import (FR 4)",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-13T16:00:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e1c278bd7e208a417e6aa193ef20506b9d47a874",
          "body": "Two of the three FRs from\nspecs/y-cluster/FEATURE_REQUEST_DISK_REUSE_E2E_PRIMITIVES.md\nthe checkit maintainer filed against the local-qemu disk-reuse\nflow. FR 2 (`--first-boot-only`) is deferred -- the spec marks\nit as needing a design pass on marker storage / mount semantics /\nverb shape, and that \n[…]\ng lives on `add` (with a\nflag-mismatch error from `replace`) or a separate verb\n`add-first-boot`. Worth a design pass before code.\n\nRefs:\n- specs/y-cluster/FEATURE_REQUEST_DISK_REUSE_E2E_PRIMITIVES.md",
          "is_bot": false,
          "headline": "feat(manifests, import): scope adds for the appliance disk-reuse PoC",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-13T12:10:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4b885726e3cf30cfcfc080c65e1b66b9d4cdceff",
          "body": "Adds an operator-facing primitive that exposes the labeled\ndata-volume mechanic the appliance flow uses in cloud, but\nthrough the local qemu provisioner. The maintainer no longer\nhas to round-trip through prepare-export + GCP / Hetzner\nimport to verify a disk-reuse scenario.\n\nSurface:\n\n  - QEMUConfi\n[…]\n  re-provision same DataDisk -> read sentinel back.\n    Closes the coverage gap where the only existing\n    appliance-volume e2e went through prepare-export +\n    seed-tarball (a different code path).",
          "is_bot": false,
          "headline": "feat(provision/qemu): DataDisk for local disk-reuse QA",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-13T08:05:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3b0ca7365a1219c2f4a1e4b82cb87c540c26559a",
          "body": "Fix digest-only image tagging and gateway resource requests, again",
          "is_bot": false,
          "headline": "Merge pull request #26 from Yolean/fix-aliasfor-and-controller-patch",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-12T10:41:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f1ae8c1f0723f01a41acfa4e36bbcac1e11a7dde",
          "body": "…ranches\n\nTestEnvoyGateway_InstallAgainstKwok was calling Install with the\nfour resource-request fields left zero, which short-circuited\nboth the kubectl-patch branch (controller) and the EnvoyProxy CR\nbranch (proxy). That's how PR #25's \"kubectl rejects the partial\nDeployment manifest\" regression s\n[…]\nfuture regression where the\npatch step generates an invalid Deployment or the EnvoyProxy CR\nisn't applied will surface here without needing a full\ndocker/qemu e2e.\n\nRefs the kubectl-patch fix b8ec43d.",
          "is_bot": false,
          "headline": "test(e2e/envoygateway): exercise patch + EnvoyProxy + parametersRef b…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-12T10:26:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7a6e69d132dd66be1d54bdaaf2cc30eddf5ce3f1",
          "body": "…gest\n\naliasFor was added in PR #23 to skip the bare sha256:<hex>\nconfig-digest row and to synthesize a :latest@digest alias for\ndigest-only refs. It missed the fourth shape, tag+digest\n(\"<repo>:<tag>@<digest>\"), which is what y-site-images-load\nemits today: every line in the input was logging\n\n  WA\n[…]\n \":\" after the last \"/\"\nto avoid host:port false positives.\n\nTestAliasFor grows the four-shape table: adds tag+digest,\n:latest+digest, and a hostport tag+digest case.\n\nRefs PR #23, /tmp/appliance.log.",
          "is_bot": false,
          "headline": "fix(images): aliasFor on tag+digest input drops the tag, keeps the di…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-12T10:18:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b8ec43da41388d0bce2d9473a30d5c691a6213ad",
          "body": "…ontroller resources\n\nkubectl apply --server-side validates the manifest against the\napps/v1 Deployment schema client-side; a partial manifest\ndeclaring only resources.requests fails with \"spec.selector:\nRequired value\" / \"containers[0].image: Required value\" before\nthe request reaches the API serve\n[…]\npinned via TestControllerResourcesPatch_RequestsOnly:\npatch body asserts neither apiVersion nor kind nor metadata,\nand rejects accidental limits / image declarations.\n\nRefs PR #25, /tmp/appliance.log.",
          "is_bot": false,
          "headline": "fix(provision/envoygateway): kubectl patch instead of SSA-apply for c…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-12T10:18:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4d70fa8b2affd843a8bb1f9116da5a29132cb817",
          "body": "…-requests\n\nGateway API reduce default resource requests and allow override",
          "is_bot": false,
          "headline": "Merge pull request #25 from Yolean/gatewayapi-reduce-default-resource…",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-12T08:16:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da717f99953c433d743b2b9ec7b9c8807e624972",
          "body": "List containerd's images: images list --context=<ctx>",
          "is_bot": false,
          "headline": "Merge pull request #24 from Yolean/images-list-from-cluster",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-12T08:14:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9e493bc6424701e55e7533e8e75f4efdae63d522",
          "body": "…e requests\n\nUpstream Envoy Gateway ships with 100m/256Mi controller and\n100m/512Mi data-plane proxy requests, which oversubscribe a\n2GB-RAM appliance node and hurt single-user dev clusters\nthat never push the proxy past idle.\n\nTwo new config knobs under gateway.resources.{controller,proxy}:\neach ta\n[…]\n\non top -- EG honours the latest reconciled spec.\n\nWire-through: docker, qemu, multipass providers all pass\ncfg.Gateway.Resources.{Controller,Proxy} to envoygateway.Install.\n\nSchema files regenerated.",
          "is_bot": false,
          "headline": "feat(provision/envoygateway): configurable controller + proxy resourc…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-12T08:12:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "550f3190a2ba8bbbc48729855ea76f5918eb5485",
          "body": "`y-cluster images list` overloads to read from a cluster's\nk8s.io containerd namespace. Mutually exclusive with the\npositional YAML input -- same conceptual question\n(\"what images are in this scope?\"), different source.\n\nOutput is one row per stored manifest (digest-aliases of\nthe same content fold \n[…]\n else. Hostport \":port\" before the last slash\nisn't counted as a tag (the standard containerd-ref edge\ncase stripTag already handles).\n\nRefs specs/y-cluster/FEATURE_REQUEST_IMAGES_LIST_FROM_CLUSTER.md",
          "is_bot": false,
          "headline": "feat(images): list --context=<ctx> queries the cluster's containerd",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-12T07:55:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "51697d783d2420abb00c8950a3dc3d0bdf314bb1",
          "body": "Fix image load issue with digest only",
          "is_bot": false,
          "headline": "Merge pull request #23 from Yolean/images-load-config-digest-refs",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-11T19:31:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e22775ae9fd64c8868ffdb4420e8558014a207ac",
          "body": "Two changes in the post-import alias step:\n\n1. The alias loop walked every new row from `ctr image list`,\n   including the bare `sha256:<hex>` config-digest row that ctr\n   writes alongside the canonical ref. stripTag turned that into\n   the literal \"sha256\", and the synthesized alias became\n   \"sha\n[…]\nin\nTestAliasFor. Summary log filters the config-digest row too, so\noperators no longer see misleading \"imported sha256:...\" lines.\n\nRefs specs/y-cluster/ISSUE_IMAGES_LOAD_MANGLES_CONFIG_DIGEST_REFS.md",
          "is_bot": false,
          "headline": "fix(images): skip config-digest rows, alias digest-only inputs",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-11T18:15:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3a4b0a1e557b599e175c2b1d107926f0e98a8fff",
          "body": null,
          "is_bot": false,
          "headline": "an attempt to get readable log lines for images load",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-11T08:26:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5129b0c12bdadcc3aada50d92347411f84de4316",
          "body": "Support remote images with images load subcommand",
          "is_bot": false,
          "headline": "Merge pull request #22 from Yolean/images-cache-support-remote",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-11T08:09:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fc238ef357450bd087bc4c83e81a5071a212134b",
          "body": "…present\n\n`y-cluster images load` accepts:\n\n  ./...  /...  ~/...      treat as local path\n                          - file: an already-tarred OCI archive\n                          - directory: an OCI v1 layout (tar\n                            streamed on the fly, equivalent to\n                      \n[…]\n stdin and path input is asserted before any kubeconfig is\n  consulted (so misuse errors don't get masked by a missing\n  context).\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(images): load <ref|path|-> -- ref support, cache reuse, skip-if-…",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-11T08:02:26Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c85c79648cd71a762bbf7bf7d86207908b6fd28c",
          "body": "Appliance primitives followup",
          "is_bot": false,
          "headline": "Merge pull request #21 from Yolean/appliance-primitives-followup",
          "author_name": "solsson",
          "author_login": "solsson",
          "committed_at": "2026-05-07T10:12:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5ada0ce3b84baeb9f429014c5d326544edeb9218",
          "body": "Previously teardown unconditionally logged \"teardown complete,\ndisk and keypair deleted\" regardless of what actually\nhappened. The os.Remove calls swallowed all errors (including\nIsNotExist) with `_ =`, so a teardown against an already-empty\ncache dir -- common after prepare-export consumed the qcow\n[…]\nract via\nzaptest/observer: empty-cache must NOT claim deletion; a cache\nwith disk + keypair must list both in the `removed` field.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(provision/qemu): teardown logs reflect actual file ops",
          "author_name": "Yolean k8s-qa",
          "author_login": null,
          "committed_at": "2026-05-07T09:43:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        }
      ],
      "releases_count": 20,
      "commits_last_year": 181,
      "latest_release_at": "2026-07-13T12:07:45Z",
      "latest_release_tag": "v0.5.0",
      "releases_from_tags": false,
      "days_since_last_push": 8,
      "active_weeks_last_year": 6,
      "days_since_latest_release": 8,
      "mean_days_between_releases": 7.8
    },
    "community": {
      "has_readme": true,
      "has_license": false,
      "has_description": false,
      "has_contributing": false,
      "health_percentage": 25,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/Yolean/y-cluster",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/Yolean/y-cluster",
          "is_deprecated": false,
          "latest_version": "v0.5.0",
          "repository_url": "https://github.com/Yolean/y-cluster",
          "versions_count": 20,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-07-13T12:07:05Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 8
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 0,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_stars": 0
      },
      "open_issues_and_prs": 2
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "example"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 40587,
      "source_files_sampled": 211,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "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": "cuelang.org/go",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.16.1"
        },
        {
          "name": "github.com/containerd/errdefs",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.0.0"
        },
        {
          "name": "github.com/google/go-containerregistry",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.21.5"
        },
        {
          "name": "github.com/hetznercloud/hcloud-go/v2",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.39.0"
        },
        {
          "name": "github.com/invopop/jsonschema",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.14.0"
        },
        {
          "name": "github.com/moby/moby/api",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.54.2"
        },
        {
          "name": "github.com/moby/moby/client",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.4.1"
        },
        {
          "name": "github.com/pkg/sftp",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.13.10"
        },
        {
          "name": "github.com/spf13/cobra",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.10.2"
        },
        {
          "name": "go.uber.org/zap",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.27.1"
        },
        {
          "name": "golang.org/x/crypto",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.54.0"
        },
        {
          "name": "sigs.k8s.io/kustomize/api",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.21.1"
        },
        {
          "name": "sigs.k8s.io/kustomize/kyaml",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.21.1"
        },
        {
          "name": "sigs.k8s.io/yaml",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.6.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": 0,
        "merged_prs": 25,
        "open_issues": 2,
        "closed_ratio": 0.333,
        "closed_issues": 1,
        "closed_unmerged_prs": 2
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "solsson",
          "commits": 41,
          "avatar_url": "https://avatars.githubusercontent.com/u/144945?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yaml",
        "e2e-release.yaml",
        "mirror-k3s.yaml"
      ],
      "has_docs_dir": false,
      "linter_configs": [
        ".golangci.yaml"
      ],
      "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": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 10,
            "reason": "1 out of 1 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/1 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 0,
            "reason": "license file not 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": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 10,
            "reason": "all dependencies are pinned",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 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": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 8,
            "reason": "2 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "6e7b7132b6641f3a831f0fe134baa698a6017df5",
        "ran_at": "2026-07-22T11:02:09Z",
        "aggregate_score": 3.9,
        "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-20T09:17:03Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-07-13T12:07:05Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 9,
          "created_at": "2026-05-01T04:38:23Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 11,
          "created_at": "2026-05-01T04:55:32Z",
          "last_comment_at": "2026-05-07T11:03:49Z",
          "last_comment_author": "solsson"
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/Yolean/y-cluster",
    "host": "github.com",
    "name": "y-cluster",
    "owner": "Yolean"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 50,
      "inputs": {
        "security": 38,
        "vitality": 73,
        "community": 12,
        "governance": 51,
        "engineering": 66
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 73,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 61,
            "inputs": {
              "commits_last_year": 181,
              "human_commit_share": 1,
              "days_since_last_push": 8,
              "active_weeks_last_year": 6
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 8 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "6/52 weeks with commits",
                "points": 4.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 6
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "181 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 181
                    }
                  }
                ],
                "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": 20,
              "latest_release_tag": "v0.5.0",
              "releases_from_tags": false,
              "days_since_latest_release": 8,
              "mean_days_between_releases": 7.8
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "20 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 8 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~7.8 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 7.8
                    }
                  }
                ],
                "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": "critical",
        "name": "Community & Adoption",
        "value": 12,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 0,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "0 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "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": "critical",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 25,
            "inputs": {
              "has_readme": true,
              "has_license": false,
              "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": "no license file detected",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "license_absent",
                    "params": {}
                  }
                ],
                "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": "moderate",
        "name": "Sustainability & Governance",
        "value": 51,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "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 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "moderate",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 51,
            "inputs": {
              "merged_prs": 25,
              "open_issues": 2,
              "closed_issues": 1,
              "issue_closed_ratio": 0.333,
              "closed_unmerged_prs": 2
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "33% of issues closed",
                "points": 15.6,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 33
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "25/27 decided PRs merged",
                "points": 35.4,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 25,
                      "decided": 27
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/1 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 62,
            "inputs": {
              "followers": 9,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "Yolean",
              "public_repos": 99,
              "account_age_days": 4165
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "9 followers of Yolean",
                "points": 7.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 9,
                      "login": "Yolean"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "99 public repos, account ~11 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 99
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 11
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "github.com/Yolean/y-cluster"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 8
            },
            "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 8 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "20 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "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": "moderate",
        "name": "Engineering Quality",
        "value": 66,
        "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": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": ".golangci.yaml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yaml"
                    }
                  }
                ],
                "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": "1 out of 1 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "at_risk",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": false,
              "has_description": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 0,
                "status": "missed",
                "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": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 38,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 38,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 17,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 1,
              "scorecard_aggregate": 3.9
            },
            "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 not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "1 out of 1 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/1 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file not detected",
                "points": 0,
                "status": "missed",
                "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 not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is 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": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "2 existing vulnerabilities detected",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 68,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.93,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "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": "93 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 93,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "excellent",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 87,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0.77,
              "toolchain_manifests": [
                "go.mod"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "go.mod (toolchain convention, no task runner)",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "toolchain_convention",
                    "params": {
                      "files": "go.mod"
                    }
                  }
                ],
                "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.yaml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yaml"
                    }
                  }
                ],
                "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": "77 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 77,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "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": 40587,
              "source_files_sampled": 211,
              "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/211 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 211,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "example"
              ],
              "has_mcp_signal": false,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "example",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "example"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-22T11:02:26.067426Z",
  "schema_version": "0.26.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/y/Yolean/y-cluster.svg",
  "full_name": "Yolean/y-cluster",
  "license_state": "absent",
  "license_spdx": null
}

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

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

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