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