Raw JSON report machine-readable
{
"data": {
"repo": {
"topics": [],
"is_fork": false,
"size_kb": 37294,
"has_wiki": false,
"homepage": null,
"languages": {
"Go": 4557490,
"CSS": 42847,
"HTML": 2332,
"NSIS": 6426,
"Ruby": 2605,
"Rust": 829048,
"Shell": 22580,
"Kotlin": 245353,
"Svelte": 1087023,
"Makefile": 20131,
"JavaScript": 612812,
"TypeScript": 6257
},
"pushed_at": "2026-07-18T18:00:21Z",
"created_at": "2026-04-01T03:30:12Z",
"owner_type": "User",
"updated_at": "2026-07-21T23:14:19Z",
"description": "A fully-featured APRS stack with modern web UI",
"is_archived": false,
"is_disabled": false,
"license_spdx": "GPL-2.0",
"default_branch": "main",
"license_spdx_raw": "GPL-2.0",
"primary_language": "Go",
"significant_languages": [
"Go",
"Svelte",
"Rust"
]
},
"owner": {
"blog": "https://chrissnell.com",
"name": "Chris Snell",
"type": "User",
"login": "chrissnell",
"company": "@adobe",
"location": "Manhattan, Kansas",
"followers": 129,
"avatar_url": "https://avatars.githubusercontent.com/u/1072626?v=4",
"created_at": "2011-09-23T00:25:23Z",
"is_verified": null,
"public_repos": 91,
"account_age_days": 5417
},
"license": {
"state": "standard",
"spdx_id": "GPL-2.0",
"raw_spdx": "GPL-2.0",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "v0.14.11",
"kind": "patch",
"published_at": "2026-07-17T05:42:34Z"
},
{
"tag": "v0.14.10",
"kind": "patch",
"published_at": "2026-06-30T16:41:47Z"
},
{
"tag": "v0.14.9",
"kind": "patch",
"published_at": "2026-06-28T04:55:42Z"
},
{
"tag": "v0.14.8",
"kind": "patch",
"published_at": "2026-06-23T00:08:17Z"
},
{
"tag": "v0.14.7",
"kind": "patch",
"published_at": "2026-06-20T16:25:37Z"
},
{
"tag": "v0.14.6",
"kind": "patch",
"published_at": "2026-06-20T04:38:50Z"
},
{
"tag": "v0.14.5",
"kind": "patch",
"published_at": "2026-06-19T03:21:31Z"
},
{
"tag": "v0.14.4",
"kind": "patch",
"published_at": "2026-06-19T03:05:45Z"
},
{
"tag": "v0.14.3-beta.1",
"kind": "prerelease",
"published_at": "2026-06-18T04:41:34Z"
},
{
"tag": "v0.14.2",
"kind": "patch",
"published_at": "2026-06-17T22:42:47Z"
},
{
"tag": "v0.14.1",
"kind": "patch",
"published_at": "2026-06-17T18:19:19Z"
},
{
"tag": "v0.14.0",
"kind": "minor",
"published_at": "2026-06-15T06:39:59Z"
},
{
"tag": "v0.13.16",
"kind": "patch",
"published_at": "2026-06-11T17:41:42Z"
},
{
"tag": "v0.13.15",
"kind": "patch",
"published_at": "2026-06-02T16:01:31Z"
},
{
"tag": "v0.13.14",
"kind": "patch",
"published_at": "2026-06-02T01:55:09Z"
},
{
"tag": "snapshot-issue-202",
"kind": "other",
"published_at": "2026-06-01T23:38:12Z"
},
{
"tag": "v0.13.13",
"kind": "patch",
"published_at": "2026-05-30T04:08:13Z"
},
{
"tag": "v0.13.12",
"kind": "patch",
"published_at": "2026-05-29T21:31:56Z"
},
{
"tag": "v0.13.11",
"kind": "patch",
"published_at": "2026-05-27T23:45:59Z"
},
{
"tag": "v0.13.10",
"kind": "patch",
"published_at": "2026-05-24T01:23:46Z"
},
{
"tag": "v0.13.9",
"kind": "patch",
"published_at": "2026-05-23T23:20:42Z"
},
{
"tag": "v0.13.8",
"kind": "patch",
"published_at": "2026-05-22T19:47:35Z"
},
{
"tag": "v0.13.7",
"kind": "patch",
"published_at": "2026-05-22T05:27:11Z"
},
{
"tag": "v0.13.6",
"kind": "patch",
"published_at": "2026-05-21T22:42:10Z"
},
{
"tag": "v0.13.5",
"kind": "patch",
"published_at": "2026-05-18T00:43:48Z"
},
{
"tag": "v0.13.4-beta.4",
"kind": "prerelease",
"published_at": "2026-05-16T19:10:00Z"
},
{
"tag": "v0.13.4-beta.3",
"kind": "prerelease",
"published_at": "2026-05-16T07:24:38Z"
},
{
"tag": "v0.13.4-beta.2",
"kind": "prerelease",
"published_at": "2026-05-09T17:33:10Z"
},
{
"tag": "v0.13.4-beta.1",
"kind": "prerelease",
"published_at": "2026-05-09T17:15:23Z"
},
{
"tag": "v0.13.3",
"kind": "patch",
"published_at": "2026-05-09T07:03:21Z"
},
{
"tag": "v0.13.2",
"kind": "patch",
"published_at": "2026-05-07T03:44:07Z"
},
{
"tag": "v0.13.1",
"kind": "patch",
"published_at": "2026-05-06T01:35:41Z"
},
{
"tag": "v0.13.0",
"kind": "minor",
"published_at": "2026-05-05T20:53:30Z"
},
{
"tag": "v0.12.6",
"kind": "patch",
"published_at": "2026-05-02T18:18:14Z"
},
{
"tag": "v0.12.5",
"kind": "patch",
"published_at": "2026-05-02T17:28:16Z"
},
{
"tag": "v0.12.4",
"kind": "patch",
"published_at": "2026-05-02T16:43:45Z"
},
{
"tag": "v0.12.3",
"kind": "patch",
"published_at": "2026-05-01T05:53:44Z"
},
{
"tag": "v0.12.2",
"kind": "patch",
"published_at": "2026-05-01T05:26:50Z"
},
{
"tag": "v0.12.1",
"kind": "patch",
"published_at": "2026-05-01T01:57:54Z"
},
{
"tag": "v0.12.0",
"kind": "minor",
"published_at": "2026-04-30T23:05:05Z"
},
{
"tag": "v0.11.4",
"kind": "patch",
"published_at": "2026-04-22T05:49:47Z"
},
{
"tag": "v0.11.3",
"kind": "patch",
"published_at": "2026-04-22T04:48:24Z"
},
{
"tag": "v0.11.2",
"kind": "patch",
"published_at": "2026-04-22T03:46:04Z"
},
{
"tag": "v0.11.1",
"kind": "patch",
"published_at": "2026-04-22T02:49:50Z"
},
{
"tag": "v0.11.0",
"kind": "minor",
"published_at": "2026-04-22T02:05:15Z"
},
{
"tag": "v0.10.11",
"kind": "patch",
"published_at": "2026-04-20T15:37:53Z"
},
{
"tag": "v0.10.10",
"kind": "patch",
"published_at": "2026-04-20T03:30:05Z"
},
{
"tag": "v0.10.9",
"kind": "patch",
"published_at": "2026-04-20T02:57:36Z"
},
{
"tag": "v0.10.8",
"kind": "patch",
"published_at": "2026-04-20T01:26:35Z"
},
{
"tag": "v0.10.7",
"kind": "patch",
"published_at": "2026-04-20T00:55:48Z"
},
{
"tag": "v0.10.6",
"kind": "patch",
"published_at": "2026-04-20T00:23:26Z"
},
{
"tag": "v0.10.5",
"kind": "patch",
"published_at": "2026-04-19T22:55:49Z"
},
{
"tag": "v0.10.4",
"kind": "patch",
"published_at": "2026-04-19T19:32:04Z"
},
{
"tag": "v0.10.3",
"kind": "patch",
"published_at": "2026-04-19T18:05:46Z"
},
{
"tag": "v0.10.2",
"kind": "patch",
"published_at": "2026-04-19T18:01:14Z"
},
{
"tag": "v0.10.1",
"kind": "patch",
"published_at": "2026-04-19T07:05:27Z"
},
{
"tag": "v0.10.0",
"kind": "minor",
"published_at": "2026-04-19T04:59:43Z"
},
{
"tag": "v0.9.20",
"kind": "patch",
"published_at": "2026-04-19T00:19:29Z"
},
{
"tag": "v0.9.19",
"kind": "patch",
"published_at": "2026-04-18T19:27:35Z"
},
{
"tag": "v0.9.18",
"kind": "patch",
"published_at": "2026-04-18T07:53:51Z"
},
{
"tag": "v0.9.17",
"kind": "patch",
"published_at": "2026-04-17T08:00:00Z"
},
{
"tag": "v0.9.16",
"kind": "patch",
"published_at": "2026-04-17T07:51:02Z"
},
{
"tag": "v0.9.15",
"kind": "patch",
"published_at": "2026-04-17T04:40:29Z"
},
{
"tag": "v0.9.14",
"kind": "patch",
"published_at": "2026-04-16T22:37:10Z"
},
{
"tag": "v0.9.13",
"kind": "patch",
"published_at": "2026-04-16T18:06:06Z"
},
{
"tag": "v0.9.12-beta.1",
"kind": "prerelease",
"published_at": "2026-04-16T15:17:59Z"
},
{
"tag": "v0.9.11",
"kind": "patch",
"published_at": "2026-04-16T05:34:33Z"
},
{
"tag": "v0.9.10",
"kind": "patch",
"published_at": "2026-04-16T03:04:27Z"
},
{
"tag": "v0.9.9",
"kind": "patch",
"published_at": "2026-04-15T23:34:33Z"
},
{
"tag": "v0.9.8-beta.5",
"kind": "prerelease",
"published_at": "2026-04-15T23:15:27Z"
},
{
"tag": "v0.9.8-beta.4",
"kind": "prerelease",
"published_at": "2026-04-15T23:03:59Z"
},
{
"tag": "v0.9.8-beta.3",
"kind": "prerelease",
"published_at": "2026-04-15T21:22:50Z"
},
{
"tag": "v0.9.8-beta.2",
"kind": "prerelease",
"published_at": "2026-04-15T21:20:34Z"
},
{
"tag": "v0.9.8-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T21:18:01Z"
},
{
"tag": "v0.9.7-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T21:10:39Z"
},
{
"tag": "v0.9.6-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T20:35:35Z"
},
{
"tag": "v0.9.5-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T20:05:13Z"
},
{
"tag": "v0.9.4-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T19:12:57Z"
},
{
"tag": "v0.9.3-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T18:36:43Z"
},
{
"tag": "v0.9.2-beta.1",
"kind": "prerelease",
"published_at": "2026-04-15T18:23:36Z"
},
{
"tag": "v0.9.2",
"kind": "patch",
"published_at": "2026-04-15T06:52:59Z"
},
{
"tag": "v0.9.0",
"kind": "minor",
"published_at": "2026-04-15T02:28:09Z"
},
{
"tag": "v0.8.10-beta.6",
"kind": "prerelease",
"published_at": "2026-04-15T02:30:01Z"
},
{
"tag": "v0.8.10-beta.5",
"kind": "prerelease",
"published_at": "2026-04-15T01:14:36Z"
},
{
"tag": "v0.8.10-beta.4",
"kind": "prerelease",
"published_at": "2026-04-14T23:01:25Z"
},
{
"tag": "v0.8.10-beta.3",
"kind": "prerelease",
"published_at": "2026-04-14T19:03:38Z"
},
{
"tag": "v0.8.10-beta.2",
"kind": "prerelease",
"published_at": "2026-04-14T17:36:49Z"
},
{
"tag": "v0.8.10-beta.1",
"kind": "prerelease",
"published_at": "2026-04-14T16:05:31Z"
},
{
"tag": "v0.8.9",
"kind": "patch",
"published_at": "2026-04-14T14:45:32Z"
},
{
"tag": "v0.8.8",
"kind": "patch",
"published_at": "2026-04-14T05:40:38Z"
},
{
"tag": "v0.8.5",
"kind": "patch",
"published_at": "2026-04-13T17:21:04Z"
},
{
"tag": "v0.8.4",
"kind": "patch",
"published_at": "2026-04-13T16:24:51Z"
},
{
"tag": "v0.8.3",
"kind": "patch",
"published_at": "2026-04-13T13:56:51Z"
},
{
"tag": "v0.8.2",
"kind": "patch",
"published_at": "2026-04-13T06:51:01Z"
},
{
"tag": "v0.8.1",
"kind": "patch",
"published_at": "2026-04-13T03:57:13Z"
},
{
"tag": "v0.8.0",
"kind": "minor",
"published_at": "2026-04-13T02:42:58Z"
},
{
"tag": "v0.7.19",
"kind": "patch",
"published_at": "2026-04-12T14:34:38Z"
},
{
"tag": "v0.7.18",
"kind": "patch",
"published_at": "2026-04-12T05:42:50Z"
},
{
"tag": "v0.7.17",
"kind": "patch",
"published_at": "2026-04-11T23:45:19Z"
},
{
"tag": "v0.7.16",
"kind": "patch",
"published_at": "2026-04-10T13:21:42Z"
}
],
"recent_commits": [
{
"oid": "6ac926a165d1ff61276be827d35dc162488cdda7",
"body": null,
"is_bot": false,
"headline": "Release v0.14.11",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T05:34:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9caf126bfdd87fe8300d533b24e3c6af337cf59f",
"body": "…ility\n\nExplain RF-reachable stations in the station popup (#482)",
"is_bot": false,
"headline": "Merge pull request #486 from chrissnell/feature/rf-only-popup-reachab…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T05:22:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "31fb6828c3a1ef605b20549dc1ffeaccb23892ee",
"body": "…me-padding\n\nPreserve object/item name padding in packet log display",
"is_bot": false,
"headline": "Merge pull request #481 from kotfu/fix/packet-log-collapses-object-na…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T05:03:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6336b5381056a0a65abe565a4a2a44629e4d19a2",
"body": "Code review noted the popup note's tooltip claimed the latest packet\n\"arrived via APRS-IS\", but rfReachableDespiteNonRfLatest also fires when\nthe latest packet is our own TX (beacon heard back on RF) or Internet-to-RF\ngated. Reword the tooltip to the truthful, general \"did not arrive over RF\n(APRS-IS or Internet-to-RF gated)\". Add tests locking in the TX-latest fire\ncase and the null/empty-positions guards.",
"is_bot": false,
"headline": "web/map: make RF-reachable note copy accurate for all firing cases",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T04:58:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f63c2d8fc37a9f958c6e863a3e2905306dcf5257",
"body": "…android-device-step\n\nFix Digirig Lite tone PTT greyed out on Android",
"is_bot": false,
"headline": "Merge pull request #380 from chrissnell/feature/fix-digirig-tone-ptt-…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T04:55:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "66c11c4775b331ed5c17d08edf4373742fd2165b",
"body": "…hannel\n\nAdd per-message TX channel override to new-message dialog (#472)",
"is_bot": false,
"headline": "Merge pull request #477 from chrissnell/feature/compose-advanced-tx-c…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T04:54:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "90e9c5fc4da2ecd832c1d67c17452162f8e85f9d",
"body": "Stop steering SignaLink USB users to serial PTT (GH #451)",
"is_bot": false,
"headline": "Merge pull request #454 from chrissnell/fix/signalink-vox-ptt-guidance",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T04:54:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1d390e25800db984ba74ea6af3d48fb21774a2dc",
"body": "…inimum\n\nEnforce a consistent minimum password length between setup and web login",
"is_bot": false,
"headline": "Merge pull request #484 from chrissnell/feature/consistent-password-m…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T04:53:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "125b60ecfc7d0b7add70bdc7a4de0945b8e87bf0",
"body": "docs: document --group-add audio for Docker sound-card access",
"is_bot": false,
"headline": "Merge pull request #485 from chrissnell/feature/docker-audio-group-docs",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T01:28:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a96dceb84248b9839c69a98c4e3d094adb9a0752",
"body": "A station heard on RF at its current fix stays visible under the Live Map\n\"RF Only\" filter even after it re-beacons via APRS-IS, because the marker\nis plotted at positions[0], which stationcache pins to the most\nRF-reachable copy of that fix (rfRank). The popup badge/via line, however,\nread the late\n[…]\n,\n node-testable module) with tests covering the IS-latest, gated-latest,\n digipeated, and non-diverging cases.\n\nNo predicate change: RF Only still keys on positions[0]. Documented in\ninvariant #52.",
"is_bot": false,
"headline": "web/map: explain RF-reachable stations in the station popup",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T00:52:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "39e5d795de66b272fe27ffb19a27e97065dd38d8",
"body": "The container image runs as a non-root user, so --device /dev/snd alone\nis not enough to open the sound-card device nodes (owned root:audio).\nWithout --group-add audio, Detect Devices finds nothing even though\narecord -l lists the card. Document this in the Docker install steps\n(run + compose) and add a troubleshooting callout on the Audio Devices\npage.",
"is_bot": false,
"headline": "docs: document --group-add audio for Docker sound-card access",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-17T00:45:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c9144037762da3edefe36d6c0b0d5e46d561416",
"body": "The web login form required a minimum of 8 characters, but the setup\npath (POST /api/auth/setup and the `graywolf auth set-password` CLI)\naccepted any non-empty password. A short password set during setup was\nthen refused at the web login screen, locking operators out of the\nconfig UI (graywolf#476)\n[…]\nd, so accounts whose password\n predates the minimum still authenticate.\n- Document the 8-character requirement in the handbook and package\n post-install output, and record the invariant in the wiki.",
"is_bot": false,
"headline": "webauth: enforce a consistent minimum password length",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-16T23:38:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3a601bb1ec99391a62c0582c0b99529a20ced051",
"body": "Revert \"Render a flat conventional map compass\"",
"is_bot": false,
"headline": "Merge pull request #483 from chrissnell/feature/revert-flat-map-compass",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-16T23:18:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "47e77fb99c9fbda44ea137a15f1c94fc0c2658ee",
"body": "This reverts commit eec28712d19643e5321d9c929d27fd6db9568f59.",
"is_bot": false,
"headline": "Revert \"web: render a flat conventional map compass\"",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-16T22:55:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "24a519d736603e3dc69f012377911bff5da6bba8",
"body": "The APRS Logs raw-packet line (.pkt-raw) used `white-space: normal`,\nwhich collapses runs of spaces to one. Object and Item reports space-pad\nthe 9-char name field, so a correctly-encoded `;PARK *...` rendered\nas `;PARK *...`, making it look like the encoder had dropped the padding.\n\nSwitch to `\n[…]\n: pre-wrap` so the padding is preserved while the\nline still wraps at the container edge. Display-only change; the encoder\nwas already correct.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
"is_bot": false,
"headline": "Preserve object/item name padding in packet log display",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-07-15T01:04:29Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "842ff39cc3b04b3d3d9453ecc7be1df64941fb01",
"body": "Render a flat conventional map compass",
"is_bot": false,
"headline": "Merge pull request #480 from chrissnell/feature/flat-map-compass",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T21:49:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "eec28712d19643e5321d9c929d27fd6db9568f59",
"body": "The live map compass used MapLibre's visualizePitch mode, which tilts the\nnorth needle into a 3D perspective as the map pitches. That reads as\nunusual next to the plain compass most maps use. Turn it off so the\nneedle stays flat and simply points north, matching the standard style.",
"is_bot": false,
"headline": "web: render a flat conventional map compass",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T21:41:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7655b068d1bc4feda08fb760ec7a3f87407b2579",
"body": "Blue dot tracks live GPS on the Live Map",
"is_bot": false,
"headline": "Merge pull request #478 from chrissnell/feature/live-blue-dot-tracking",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T19:57:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "012a419b2347be01974e3dcad4361ec2a67ae747",
"body": "Dedupe the own-position poll so a visibility catch-up firing mid-tick\ncannot race a scheduled poll and let the older of two near-identical\nfixes win. Document why the recenter effect's didAutoFit guard is now\nload-bearing (myPosition refreshes every tick).",
"is_bot": false,
"headline": "Guard against overlapping /api/position fetches",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T19:52:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e0ca5e8b2ce9a57a1869dc2914c87d92dba2b84b",
"body": "- Fix multi-part sends transmitting later parts on the default channel:\n ComposeBar calls onSend/close once per part, and resetting the channel\n in close() zeroed it after part 1. Reset on the modal's open transition\n instead so the choice is stable across the whole batch.\n- Reject a packet-mode channel override with 400 up front (same guard as\n putMessagesConfig) instead of letting it fail RF or silently fall back\n to APRS-IS. Add handler tests for the rejection and channel pass-through.",
"is_bot": false,
"headline": "Address code review on per-message TX channel override",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T18:29:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7893bf9142c11e2550544d82ed4315df428883a3",
"body": "The Live Map fetched /api/position only once at page load, so the\nown-position marker froze at the fix present when the map opened and\nnever moved as the operator travelled -- even though the server tracks\nlive GPS continuously. Refresh the position on each poll tick (and on\nthe visibility catch-up) so the blue dot follows the current fix.\n\nFixes #473",
"is_bot": false,
"headline": "Poll own position so the map blue dot tracks live GPS",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T18:26:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "117d7ee3ffe93290b2e7c379c94a9f2abe17ea37",
"body": "aur: add alsa-lib and protobuf to makedepends (#474)",
"is_bot": false,
"headline": "Merge pull request #475 from chrissnell/fix/aur-pkgbuild-build-deps",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T18:18:13Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "49902be817e9c81c2c1ac3b9828c8efd9a92360b",
"body": "Operators running multiple radios on one instance can now pick which RF\nchannel an individual message transmits on, from a collapsed Advanced\nsection in the new-message dialog. Defaults to the configured TX channel\nso the normal compose flow is unchanged.\n\nThe chosen channel is persisted on the mess\n[…]\ne\nthe same channel as the initial send. The sender resolves the per-row\nchannel with fallback to the live default; packet-mode refusal and RF\navailability now check the effective channel.\n\nCloses #472",
"is_bot": false,
"headline": "Add per-message TX channel override to new-message dialog",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T18:16:18Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "4ba08d76e014372a8017f038a6a2c0d4336f57d2",
"body": "The graywolf-modem Rust crate builds against ALSA (via cpal) and generates\nprotobuf bindings with prost-build, which requires protoc at build time.\nWithout alsa-lib and protobuf in makedepends the AUR build fails.\n\nFixes #474",
"is_bot": false,
"headline": "aur: add alsa-lib and protobuf to makedepends",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-14T18:00:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "029550e4fa73130e0e7a6e1661b575640e3c79ad",
"body": "Add Icom IC-7100 configuration for Raspberry Pi Zero 2W",
"is_bot": false,
"headline": "Merge pull request #471 from taco40sauce/add-ic7100-PI-ZERO-2W-config,",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-12T07:59:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a119b5c9459c6c9b7f2a0714cdb0c30e7d7dd411",
"body": "Indent and space the separator comment to match the other cards, drop a\ntrailing-whitespace line, and add blank lines between config sections.",
"is_bot": false,
"headline": "Tidy whitespace in IC-7100 config card",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-12T07:58:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7ff0cbbec0e208cf9b1beb0080f3665b55b72006",
"body": null,
"is_bot": false,
"headline": "debian download/install documentation improvements (#464)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-07-12T07:33:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5778665cc4a6fd2853c6721966dea65a83d3fcfa",
"body": "Adds a known-working configuration for the Icom IC-7100 + Raspberry Pi Zero 2W running as a combined iGate + Digipeater. Includes audio device settings (with rationale for the -12dB output pad given the IC-7100's USB codec gain quirk), PTT via Hamlib rigctld/CAT (and a note on the correct vs. an eas\n[…]\nincludes a tested comparison of FM vs. FM-D modes (no measurable difference in this setup) and a note on the graywolf-modem subprocess's self-healing behavior during transient USB audio stream errors.",
"is_bot": false,
"headline": "Add Icom IC-7100 configuration for Raspberry Pi Zero 2W",
"author_name": "taco40sauce",
"author_login": "taco40sauce",
"committed_at": "2026-07-12T00:28:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "629284a2445a93539e004bb8141f3b60d4f57825",
"body": "The chonky Input carries a 1rem bottom margin from the stacked-form\ndefault. In the single-row add form, that inflates the input wrapper so\nalign-items:center dropped the Block button below the two fields. Zero\nthe input margin within .block-add so the fields and button align.",
"is_bot": false,
"headline": "Fix Block button alignment on Messages blocklist form (#470)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-11T00:20:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "eb3551cbf9646142bdf757782df7db5bf4681880",
"body": "* Add call-sign blocklist for messages (#465)\n\nOperators can now mute inbound APRS messages from specific call signs.\nA blocked sender's traffic is dropped in the router's classify step\nbefore persistence and auto-ACK, so its messages never reach the inbox\nand graywolf never acknowledges them -- the\n[…]\n: \"int32\" on integer path params; CI pins swag@v1.16.4, which\nomits them, so docs-check flagged drift across many existing endpoints.\nRegenerate swagger.json/yaml and api.d.ts with the pinned version.",
"is_bot": false,
"headline": "Add call-sign blocklist for messages (#465) (#466)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-10T22:48:11Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "851851e1c124573f5dd6e880e8a6dd232c17213d",
"body": "* Add per-interface KISS connected-mode passthrough\n\nKISS clients could only send UI frames; non-UI (connected-mode) AX.25\nframes were dropped (\"dropping non-UI frame from kiss client\"), so\nconnected-mode packet apps like Pat/Winlink could not use graywolf as a\nKISS modem even though the modem itsel\n[…]\n allow_connected_mode bypasses the aprs-only refusal that\nax25conn.Manager.Open enforces. Note the exception in invariant #23,\nexpand the kiss code-map entry, and tighten the ServerConfig doc comment.",
"is_bot": false,
"headline": "Expose connected-mode AX.25 through KISS (allow_connected_mode) (#469)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-10T22:33:28Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9bed72ee05a3eab27754781ca7586f665b386bee",
"body": "…(#468)\n\n* Recover the live map from an unrecoverable WebGL context loss (#461)\n\nNavigating to the live map created a fresh MapLibre map (a new WebGL\ncontext) every visit and tore it down on leave. Browsers cap the number\nof live WebGL contexts per page, and maplibre-gl 5.x nulls map.style when\nit l\n[…]\n a\n handful of unrelated context losses over a long operator session each\n recover instead of exhausting the 3-try cap over the page lifetime.\n\n---------\n\nCo-authored-by: chrissnell <chris@0x1b.com>",
"is_bot": false,
"headline": "Recover the live map from an unrecoverable WebGL context loss (#461) …",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-10T22:31:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "61071245735d8e82017bd155a7d2523493b26b98",
"body": "The per-conversation routing menu holds local send-path and\nresend-until-ack state. Svelte reused the component instance when only\nthreadKey changed, so opening one conversation's routing carried the\nprevious conversation's overrides. Wrap each mount in {#key threadKey}\nso switching conversations remounts the menu with default state.",
"is_bot": false,
"headline": "Fix conversation routing menu leaking state across threads (#460)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-08T06:49:55Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0274d03e82161753efc7e71b1756647725accfe0",
"body": "Turn the unset-callsign indicator on the beacon card into a link to\n#/callsign so operators can jump straight to the page that sets the\nstation callsign. Keeps the danger-red styling and adds an underline\nto signal it is clickable.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Link '(Callsign not set)' to the callsign settings page (#459)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-07T05:22:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c8a3cdaff38081ffc053af9c159797f240b64fd2",
"body": "The beacon card previously rendered a muted '(not set)' when neither\nthe beacon nor station callsign was configured. Make it read\n'(Callsign not set)' in the danger color so operators notice the\nmissing callsign.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Show '(Callsign not set)' in red when beacon callsign is unset (#458)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-07T05:08:07Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "06eb96eea5efb5a75962ee8f0fdbc84856f5f8ab",
"body": "…#457)\n\nAddresses graywolf #456:\n\n- ax25conn sessions now self-remove from the manager on any terminal\n transition into DISCONNECTED. Previously the session goroutine parked\n in DISCONNECTED forever, leaking a goroutine and a manager slot on\n every disconnect and causing a reconnect to the same p\n[…]\nari reports the GS control\n character for e.key under Ctrl).\n- The Advanced timing and windowing settings (N2 retries, T1/T2/T3,\n paclen, window, modulo, backoff) persist per-device across sessions.",
"is_bot": false,
"headline": "Fix AX.25 Terminal connect failures, stale session, and Ctrl-] menu (…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-07T02:14:55Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "d9dafc6f63f5116c0c5eed33b7b26635925199ad",
"body": "* Add per-conversation send path and resend control (GH #453)\n\nLet individual message threads override the global send path and disable\nack-driven resends, without cluttering the compose UI. Both knobs live in\na single gear popover in the thread header and default to inheriting the\nglobal MessagePre\n[…]\nols\n disabled (and toasts) so the operator can't overwrite a stored override\n with the default state shown while offline.\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Per-conversation send path & resend control (#453) (#455)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-06T22:34:18Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c735806c888780ae245e23152d05dd174a9c900c",
"body": "The SignaLink USB has no serial port; it keys the radio with an internal\nVOX circuit driven by transmit audio. Two spots implied otherwise:\n\n- pkg/pttdevice/usb_devices.go named the CP2102 (10c4:ea60) chipset\n \"(SignaLink, Digirig)\". That chip is the Digirig Mobile's; the\n SignaLink USB contains n\n[…]\nSB as a serial-RTS\n device.\n\nCorrect both, and add a VOX-section callout telling SignaLink users to\nselect the vox method (no PTT device), since that is the only way to key\na SignaLink from graywolf.",
"is_bot": false,
"headline": "Stop steering SignaLink USB users to serial PTT (GH #451)",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-07-06T21:06:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3631417c14e97cdc6ee4b76197e11468ec1dcba6",
"body": "…(#435)\n\n* android: feed real status-bar inset to CSS so top bar clears it (GH #390)\n\nThe first GH #390 fix had the mobile top bar reserve the status-bar strip\nvia env(safe-area-inset-top), populated by viewport-fit=cover. That works\non iOS but not in Android WebView, which derives env(safe-area-ins\n[…]\np-side override covers it meanwhile.\n\nAlso use ceil instead of round when converting the inset to CSS px so a\nsub-pixel never under-reserves the strip and lets the bar creep back under\nthe status bar.",
"is_bot": false,
"headline": "Fix Android top nav bar rendering behind system status bar (GH #390) …",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-03T17:34:16Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "91e5d24b6056961a601082cf673d1554202f837d",
"body": "Operators routinely read the 'Modem' label as 'my device is a modem' and\npick it for a hardware TNC/LoRa modem, which registers no TX backend and\nsilently fails transmit with 'no backend registered for channel'. Mode\nactually names which side provides the radio link, not the device kind.\n\n- Add pare\n[…]\nfrom Modem mode, naming the exact error they\n would otherwise hit.\n\nMirrors the handbook Mode section. Web build and unit tests pass.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "ui(kiss): clarify Modem vs TNC mode wording (#448)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-02T18:22:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0eeb8ef68f3743278beb9dc594c3ce4547329024",
"body": "… (#447)\n\nThe Modem/TNC mode choice is the most common KISS setup mistake: a\nhardware TNC or LoRa modem left in Modem mode connects and receives fine\nbut silently fails TX with 'txbackend: no backend registered for\nchannel', because Modem mode expects a software modem on the channel.\n\n- Rewrite the \n[…]\nwn-working\n devices (USB-C, not GPIO UART), a udev symlink tip to pin the device\n node, and the no-backend error to troubleshooting.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "docs(handbook): clarify KISS Modem vs TNC mode and hardware-TNC setup…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-02T18:22:25Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0af3cce9aed83ad865c1249a2ee9a9ab7d626149",
"body": "* Add design spec for Live Map direct-RX heatmap layer\n\n* Add implementation plan for direct-RX heatmap layer\n\n* Add rx_events store and GET /api/heatmap endpoint\n\n* Add direct-RX heatmap layer to the Live Map\n\n* Document direct-RX heatmap layer in the wiki\n\n* Fix heatmap double-count and timer leak\n[…]\nne source of truth),\nand lastHBitDigi now skips generic path aliases to match CountHops so a\nreal digi isn't masked by a trailing WIDE*. Also clean up the heatmap\npolling timer + layer on map unmount.",
"is_bot": false,
"headline": "Add direct-RX heatmap layer to the Live Map (#446)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-02T17:45:29Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "fc206518123e1043ee2e8857bcd8a31e262bca36",
"body": "Explain that either Graywolf or the TNC can act as the digipeater/iGate\nbut not both, and recommend disabling digipeating on the TNC so Graywolf\nserves as the digi.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Clarify TNC-mode KISS digipeater warning (#442)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-07-01T17:31:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5436a28484ebe389fe5d2cd2ec2f36fd4f021d1e",
"body": "A station's own beacon position only ever reached the Live Map's\nstation cache through the RF transmit path: the governor TX hook in\nwiring.go re-extracts each frame as it hits the air and feeds it into\nthe cache. An APRS-IS-only beacon (the natural config for a radioless\nor RX-only iGate) bypasses \n[…]\n\nin the wiki: a station's own beacon reaches the map via the transmit\npath, so every transmit leg must feed the station cache.\n\nCo-authored-by: chrissnell <1072626+chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Show APRS-IS-only beacons on the local map (#438) (#440)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-06-30T23:15:32Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7e93c278359da6d1088d120e8cb70fecbe470da3",
"body": "The release bump regenerated the spec with a swag build that emits\nformat: int32/float64 on numeric fields, which the docs-check drift\nguard rejects. The API is unchanged this release, so restore the\ncommitted spec to its canonical form.",
"is_bot": false,
"headline": "Restore canonical OpenAPI spec for v0.14.10",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-30T16:38:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b4beb66c9183125d7a0c72f0eeb9fa59eba03f53",
"body": null,
"is_bot": false,
"headline": "Release v0.14.10",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-30T16:33:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "236412e098c62bfbdd16d600e2a405d4f417e444",
"body": "AX.25 connected-mode BBSes are half-duplex and don't echo keystrokes;\nthe xterm canvas did no echo of its own, so operators couldn't see what\nthey typed. Restore the classic TNC ECHO ON behavior: echo operator\nkeystrokes locally on the outbound path (Enter->CRLF, backspace rubout,\ncontrol/escape seq\n[…]\nt\nunchanged.\n\nLocal echo defaults on per session and is toggleable via the Ctrl-]\n\"echo on|off\" command for the rare host that echoes.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Add local echo to AX.25 terminal so typed commands are visible (#441)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-30T16:25:45Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "65cec4c04209885c44405a75b9f12da7c9c50efc",
"body": "* Prompt a reload when the server is upgraded under an open tab\n\nWhen an operator upgrades graywolf on the server while a browser tab is\nopen, the tab keeps running the old embedded SPA against the new server\nindefinitely. The client reads /api/version exactly once at load and\nnever re-checks, and t\n[…]\nition (which the existing reduced-motion guard suppresses).\n\n---------\n\nCo-authored-by: Claude Opus 4.8 <noreply@anthropic.com>\nCo-authored-by: chrissnell <1072626+chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Prompt a reload when the server is upgraded under an open tab (#433)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-06-30T16:01:56Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "342ff02806ab446efbdd7c0cef9ff66e99011ca8",
"body": "* Fix auth set-password flag order in Windows docs and add CLI hint\n\nThe Windows install docs showed `graywolf.exe -config X auth set-password`,\nwhich fails: main.go only recognizes a subcommand as os.Args[1], so a flag\nplaced before it sends `auth ...` into the daemon flag parser, which reports\n\"un\n[…]\n it in app.Subcommands + IsSubcommand so the hint can't drift\nout of sync, and cross-reference it from the dispatch switch.\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Fix auth set-password flag order in Windows docs and add CLI hint (#439)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-30T15:50:31Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9d620e0723c2f021cd34dc2eadd16695f0fee54e",
"body": "…agement tab (#411)\n\nCapture the operator's decisions: the MCP server uses the streamable-HTTP\ntransport (not stdio) so Graywolf owns its lifecycle; a modembridge-style\nsupervisor plus an mcp_server config singleton drive a persisted on/off\ntoggle; bind localhost by default with optional, token-gate\n[…]\nnd offers an\ninner tabbed, click-to-copy set of per-agent client configs (Claude Code,\nClaude Desktop, Cursor, VS Code, Windsurf, Cline, Zed, generic). Folds into\nM4; updates sections 4.5, 11, 12, 13.",
"is_bot": false,
"headline": "docs: settle MCP server deployment -- HTTP transport, supervisor, man…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-29T19:41:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f74ff5f005da082c8eba3099984f18dfd31ee788",
"body": "Add a Monitoring section covering what the Bad FCS counter means, why it\nis a signal-quality trend rather than an exact packet count, how to judge\nwhether a value is high (ratio and trend, not raw number), and the\ndiagnostic steps for poor FCS performance.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "docs(handbook): explain the Bad FCS dashboard metric (#437)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-29T04:03:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7ac6a7be5c6a52bceb826fdc248436060de54973",
"body": "* Make landscape mobile a first-class map layout\n\nOn small landscape phones the full desktop sidebar stayed visible,\neating most of the map width (GH #419). Treat landscape phones as a\ncompact layout: the sidebar collapses to a slim vertical icon rail on\nthe left, and the map uses the FAB + bottom-s\n[…]\nbottom resets so no portrait properties bleed through.\n- Add compactLayout.test.js pinning the breakpoint string, so a JS-side\n edit that drifts from the CSS mirrors is a deliberate, reviewed change.",
"is_bot": false,
"headline": "Make landscape mobile a first-class map layout (#436)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-29T04:00:10Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "86d4ee1fc8a220f9179b1facc94f923e08240678",
"body": "Object beacons were built with an empty timestamp argument, so the\nencoder substituted the literal \"111111z\" placeholder. That is a fixed\nvalue (\"the 11th at 11:11 UTC\"), so once wall-clock time drifts away\nfrom it, APRS-IS and APRS.fi reject the object as a delayed/out-of-order\npacket and it never \n[…]\nhavior is deterministic under test. ObjectInfo's empty-string fallback\nis left intact for back-compat; the builder now always supplies a value.\n\nCo-authored-by: Claude Opus 4.8 <noreply@anthropic.com>",
"is_bot": false,
"headline": "Fix object beacons sending a fixed 111111z timestamp (#412) (#434)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-06-29T03:42:38Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "80fddd97cac6e57c2cb6394eba3ff5dc504676e0",
"body": "The Rust modem already counts FCS failures per channel and ships them in\nStatusUpdate.rx_bad_fcs, but UpdateFromStatus dropped the field on the\nfloor, so bad frames never surfaced in /metrics.\n\nAdd graywolf_rx_bad_fcs_total{channel}, fed by the same absolute-counter\ndelta translation used for graywo\n[…]\nreport it; KISS-TNC\nchannels stay absent because a hardware TNC validates the FCS itself and\nnever forwards a bad frame over KISS (issue #132).\n\nCo-authored-by: Claude Opus 4.8 <noreply@anthropic.com>",
"is_bot": false,
"headline": "Add per-channel bad-FCS Prometheus metric (#431)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-06-29T03:13:12Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "bfe0a040778653e47752c412c9f60aee4a994060",
"body": "Comment out the surface-fronts overlay wiring on the Live Map so the\nlayer no longer mounts, polls, or appears in the Weather toggle list.\nThe fronts layer/source modules and polling helpers are left intact so\nthe layer can be re-enabled by uncommenting the marked blocks.",
"is_bot": false,
"headline": "Disable Fronts map layer (comment out for now) (#427)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-29T03:07:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "acad4a09d3c40e9b18d5738d3fbd003e97184492",
"body": null,
"is_bot": false,
"headline": "Release v0.14.9",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T04:46:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e51ddfcfebf613363d5125e08996074ff5030ef9",
"body": "* Expose tracker beacon type in the UI to enable SmartBeaconing\n\nSmartBeaconing only engages when a beacon's type is 'tracker' (the\nscheduler's isSmart gate), but the beacon setup form only offered\n'position' and 'object'. A user could enable the global SmartBeaconing\npanel and have a working GPS, y\n[…]\nrve params the API would reject.\n- Document the three-gate SmartBeaconing rule and the UI auto-enable\n coupling as invariant #55 so future agents find it when debugging\n 'SmartBeaconing won't fire'.",
"is_bot": false,
"headline": "Expose tracker beacon type in the UI to enable SmartBeaconing (#428)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T04:35:15Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7e0dfb4d5ca02d105f2b093d9b44af95d8f16449",
"body": "… (#422)\n\n* fix(map): keep coordinate readout clear of status bar on narrow displays\n\nOn narrow viewports the centered map status bar grows wide enough to\nreach the bottom-right coordinate/zoom readout and hide it. Lift the\nreadout above the status bar below 900px so the two stack instead of\noverlapping.\n\nCloses #418\n\n* docs(map): cross-reference coupled status-bar/coord offsets\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Fix coordinate display overlapping map status text on narrow displays…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T04:32:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "88b1987827a87b59cbc7a4ee833125c0253e7831",
"body": "…) (#426)\n\n* Fix map track doubling back on duplicate and out-of-order fixes\n\nThe station cache prepended every moved position fix in arrival order and\nonly deduplicated against the head fix. On APRS-IS and digipeated feeds,\nduplicate and out-of-order copies of a beacon are routine, so a delayed\ncop\n[…]\nust still be\ndeduplicated by location + window rather than prepended. This documents\nwhy the scan cannot be skipped for newest-timestamp fixes.\n\nNote the history-DB un-dedup behavior in invariant #53.",
"is_bot": false,
"headline": "Fix map track doubling back on duplicate and out-of-order fixes (#421…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T03:07:30Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b2a2cab5caf867bbe17f7230ccd5af244a7f49bf",
"body": "…) (#425)\n\n* fix(map): keep callsign trail visible when newest fix leaves the viewport\n\nMap-viewport membership was decided on the head position (positions[0])\nalone, on both ends of the live-map pipeline. When a moving station's\nnewest fix left the visible bbox, the server stopped returning the\nsta\n[…]\n/empty safety. Locks in the server/client symmetry the\nGH #413 fix depends on (invariant #53).\n\nAddresses code-review follow-up: the server side had a regression test but\nthe client predicate did not.",
"is_bot": false,
"headline": "Keep callsign trail visible when newest fix leaves the viewport (#413…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T02:41:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "30824baa5eb72b13421f9f951b7b833d20b4288c",
"body": "* Add manual coordinate entry to fixed point dialog\n\nThe fixed point dialog now shows editable latitude/longitude fields\npre-filled with the clicked location, so operators can type exact\ncoordinates instead of relying only on map clicks. Accepts decimal\ndegrees with an optional N/S/E/W hemisphere le\n[…]\npath.\nWire aria-invalid and aria-describedby on the inputs so the inline\nparse error is announced and tied to the fields. Add a round-trip test\nfor the seeded value plus whitespace/leading-plus cases.",
"is_bot": false,
"headline": "Add manual coordinate entry to fixed point dialog (#424)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T02:16:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "cf1c2c4d33e1d2bb481d12737f6703a8ec569e30",
"body": "Document the working configuration from issue #420: USB audio device\nselection with an 8000 Hz sample rate for the Pi Zero 2W, Serial RTS PTT\non /dev/ttyUSB0, the full set of recommended Baofeng UV-5R menu settings\nfor open-squelch APRS, and a step-by-step audio input level procedure.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "handbook: add Baofeng UV-5R + Digirig Mobile + Pi Zero 2W config (#423)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-28T02:06:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b2971cad78136db776eb0372edd86842ee8d406a",
"body": "writing-plans output for phase M2 (GRA-206): the OsAudio capture-control\nlayer (level dB + mute) across Linux ALSA, Linux PipeWire/Pulse, macOS\nCoreAudio, and Windows WASAPI, plus the --capture-control/--set-capture-db/\n--set-capture-mute CLI. Tasks 1-5 (trait, Linux back ends, dispatch, CLI)\nare fu\n[…]\n7 (CoreAudio, WASAPI)\ngive the exact API contract and are verified on-platform. Grounded in the\nvendored alsa mixer (MilliBel/Selem), the public alsa_card_token mapping,\nand the in-tree windows crate.",
"is_bot": false,
"headline": "docs: M2 implementation plan -- OS audio control layer (#414)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T15:01:43Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e351352fd602d9f84a8a73ade90d752b452f2381",
"body": null,
"is_bot": false,
"headline": "fix(map): color pressure centers L=blue, H=red per request (#415)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T14:53:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e21eded723f647914e42f9d6d5fe5e7a86424629",
"body": "…orer) (#410)\n\n* test(modem): fix ptt build_driver test that fails to compile under cargo test\n\nbuild_result.unwrap_err() requires the Ok type (Box<dyn PttDriver>) to be\nDebug, which it isn't, so the lib-test target failed to compile. Use\n.err().expect() to extract the error without needing Debug on\n[…]\nandy\naudio-verification tool (no MCP mention).\n\nWiki (glossary.md): a Diagnostics entry for the offline record/decode helpers\npointing at src/record.rs / src/decode.rs / src/wavio.rs and the handbook.",
"is_bot": false,
"headline": "M1: graywolf-modem --record and --decode (offline capture + decode sc…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T14:17:42Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "2e59a629914766b0e60dc7a129a1f0838b0d06e8",
"body": "… (#407)\n\nAdds the writing-plans output for GRA-206: a phased roadmap (M1-M5) with\nPhase 1 (the graywolf-modem --record and --decode subcommands) fully\nspecified as bite-sized TDD tasks grounded in the crate (soundcard::spawn,\nAfskDemodulator, take_frames/take_bad_fcs, hound WAV I/O). M2-M5 get their\nown plans as each phase begins.",
"is_bot": false,
"headline": "docs: phased implementation plan for the audio auto-tuning MCP server…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T05:45:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dd7fb4171b397cfa5f0f0eb09be1b20fc8faede9",
"body": "…8)\" (#409)\n\nThis reverts commit 8c3c69fae5d4f4d6094bd33a38f2a7e255b1877b.",
"is_bot": false,
"headline": "Revert \"Dashboard: make packet rates a 10-minute rolling average (#40…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T05:10:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1bb6e3b9041b1ad2f49e9f19fc3c395bb23f5880",
"body": "…(#402)\n\n* feat(map): thicker front lines, bigger pips, drop bare stationary fronts; group layers pane\n\nFronts: line-width 1.2-2.6 -> 1.8-3.8 (troughs' dashes scale with it), pip\nicon-size 0.7-1.0 -> 0.95-1.35 with a touch more spacing. Hide stationary\nfronts entirely -- without alternating opposite\n[…]\n Curvature thresholds are live-tunable (tune({pipWindow,\npipMaxTurn})). 11 web tests pass (incl. a hairpin-exclusion test).\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "feat(map): front styling tweaks + layers-pane reorg (APRS / Weather) …",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:55:04Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8c3c69fae5d4f4d6094bd33a38f2a7e255b1877b",
"body": "Replace the lifetime average packets/hour figure with a trailing\n10-minute window: sample the cumulative RX/TX counters on each status\npoll and divide the delta by elapsed wall-clock time, so the rate\ntracks recent activity. Counter resets (station restart) and dropped\nconnections restart the window. Label the figure '10m avg' with a\ntooltip so operators know what it represents.\n\nWindow math is extracted to lib/packetRate.js with unit tests.",
"is_bot": false,
"headline": "Dashboard: make packet rates a 10-minute rolling average (#408)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:53:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fa6519874526a8875f45d196fe368356f484128b",
"body": "…int (#406)\n\nThe hosted-runner image preinstalls Microsoft/Azure apt repos that\nintermittently return 403 ('no longer signed') and fail apt-get update,\nwhich broke the Example Scripts Lint job (and any PR that ran it) while\nonly shellcheck from Ubuntu's own mirror was needed. Remove those\nthird-party source lists before update so an unrelated outage can't fail\nthe job; Ubuntu's own deb822 sources are untouched.",
"is_bot": false,
"headline": "ci: stop Microsoft/Azure apt repos from flaking the example-scripts l…",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:48:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c007d18056fbc980d5c99eaf24a63f64de35415",
"body": "Display average packets/hour, derived from the cumulative RX/TX\ncounters and station uptime, below the Packets RX and Packets TX\ntotals on the dashboard.",
"is_bot": false,
"headline": "Dashboard: show packet RX/TX rates beneath total counts (#403)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:42:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a0e314dd8f87587401662092452e5e9d0bc744ec",
"body": "…(#404)\n\n* feat(map): render model-derived global fronts beneath the WPC overlay\n\nAdds a second GeoJSON source (fronts-world, /fronts/world/latest.geojson) under\nthe existing single Fronts toggle, drawn beneath the WPC layers so analyst\nfronts win over North America and the model shows through elsew\n[…]\nnd a new test\n asserts every world layer renders beneath every WPC layer; refresh() test now\n also checks the world source/layers re-add after a style swap.\n- Hoist the doubled firstSymbolId() call.",
"is_bot": false,
"headline": "Render model-derived global fronts overlay (beneath WPC, one toggle) …",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:36:37Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "800740b0ae7afff66ff83791c77b1d83528a1b3d",
"body": "Design spec for an MCP server that auto-tunes RX audio levels for optimal APRS packet decoding (GRA-206). Docs-only.",
"is_bot": false,
"headline": "Design: cross-platform audio auto-tuning MCP server (#405)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-25T04:29:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "830ecb8faedfc574f6c87a378c7b2e6c2e63d6a1",
"body": "* Detect Windows audio enhancements and warn in Audio Devices tab\n\nWindows \"audio enhancements\" (system effects / APOs such as Loudness\nEqualization, Bass Boost, and noise suppression) sit between the radio\nand the modem and corrupt the AFSK/packet waveform, degrading or\nbreaking decoding. The modem\n[…]\navailable -> UI, which surfaces a\nbanner, a per-detected-card badge, and a per-configured-device warning.\nAlways false on Linux/macOS.\n\n* Document Windows audio enhancements detection in code-map wiki",
"is_bot": false,
"headline": "Detect Windows audio enhancements and warn in Audio Devices tab (#401)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T22:12:19Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8a90baa89fbde6e4e587ee565f282c8a7954f53c",
"body": "* feat(map): surface-fronts overlay with WMO symbology + layers-pane toggle\n\nGeoJSON source + line/pip/H-L symbol layers (cold triangles, warm semicircles,\ndashed troughs, H/L glyphs with pressure labels), a Fronts toggle defaulted on,\nand full LiveMapV2 wiring with a 5-min manifest poll. Stationary\n[…]\neach sprite (front-cold/warm/occluded) at\nregistration with sdf:false and drop icon-color. 380 web tests pass; build green.\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Surface fronts overlay — map client layer + toggle (#393)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T21:53:14Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6849d1769faba7fa771d58467912e437d5d9538b",
"body": "…(#391)\n\n* android: keep top bar below the status bar on edge-to-edge (GH #390)\n\nThe v0.14.8 edge-to-edge change padded the WebView by the top system-bar\ninset, but the SPA's mobile top bar is position:fixed; top:0. A fixed\nelement is pinned to the visual viewport, which WebView top-padding does\nnot\n[…]\nn the\nremote-actions drawer, which is position:fixed; top:0 and full-width on\nphones. Reserve the status-bar and nav-bar safe areas in CSS so its header\nand close button stay clear of the system bars.",
"is_bot": false,
"headline": "android: keep top bar below the status bar on edge-to-edge (GH #390) …",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T21:13:44Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "abddd559ec47f53a51e5757d9293c2cf1afe0364",
"body": "* Fix RF Only map filter still showing APRS-IS stations\n\nThe RF Only predicate scanned a station's entire accumulated position\ntrail and kept it visible if any fix was ever heard on RF. The live map\ndata store builds that trail by prepending each delta fix, so a mobile\nstation heard on RF earlier an\n[…]\nthat RF Only keys on positions[0] precisely to keep that RF-reachable\nstatic station visible, and that the popup badge may then read APRS-IS.\n\nAdd tests covering TX exclusion and gated-wins-over-hops.",
"is_bot": false,
"headline": "Fix RF Only map filter still showing APRS-IS stations (#394) (#399)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T17:02:17Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "d84897c42a555819daf79f2f844410fe903ba025",
"body": "The iGate TX Channel picker only listed channels and filtered them by\nTX-capability, so there was no way to select \"none\". An operator who\nset up a TX channel for testing and then removed the output device\ncould not revert the iGate to RX-only: the saved tx_channel kept\npointing at a now-non-TX-capa\n[…]\nthe loaded\ntx_channel onto the first available channel so a saved 0 round-trips\nas none instead of silently re-arming TX.\n\nCloses #396\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Allow clearing the iGate TX channel to RX-only (#400)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T16:51:53Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "98b8a404a4c906532b1a6ec272848c585f49f120",
"body": "* Fix AX.25 terminal line overlap by normalizing inbound CR/LF\n\nPacket-radio BBSes terminate lines inconsistently: FBB/TNC hosts emit a\nbare CR, JNOS/Linux hosts emit bare LF, and some emit CRLF. xterm runs\nwith convertEol:false (it only rewrites bare LF, never bare CR), so a\nbare-CR stream returned\n[…]\nunks so a CRLF split at a\nWebSocket chunk boundary stays a single line break.\n\n* docs(wiki): note terminal CR/LF normalizer in code map\n\n* docs(wiki): note bare-CR overwrite is intentionally collapsed",
"is_bot": false,
"headline": "Fix AX.25 terminal line overlap (CR/LF normalization) (#397)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T16:48:57Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "92399cbd0484274bef79554b945e9bd35e428513",
"body": "The time-range selector on the live map seeded its value from the data\nstore default (always 1 hour) and never wrote selections back, so a\nnon-default range was lost on reload. Seed it from the persisted\nmapState.timerange preference instead and write changes back to it.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Persist map layers time range across navigation and reload (#398)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-24T16:37:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "02df65ba7d12630157a0078124a19eabc11631de",
"body": "* Add digirig lite config from Jacob KB3OMS\n\n* fix errant id",
"is_bot": false,
"headline": "Add digirig lite config from Jacob KB3OMS (#389)",
"author_name": "kotfu",
"author_login": "kotfu",
"committed_at": "2026-06-23T03:30:50Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8233012529c5f8b073275f39d145c71463b5857f",
"body": null,
"is_bot": false,
"headline": "Regenerate OpenAPI spec with swag v1.16.4 to fix drift guard",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T23:59:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "df7cc50e267843d6ee1e68f8cb0b7f392e910656",
"body": null,
"is_bot": false,
"headline": "Release v0.14.8",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T23:45:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9de8f9925d6c513913cd100917150553f86b4ac7",
"body": "* Add a Hex bytes toggle for non-printable bytes in the packet log\n\nThe non-printable byte tokens (<0x7f>) from #384 are noise for everyday\nlog reading, useful only when diagnosing a malformed packet. Gate them\nbehind a device-local 'Hex bytes' toggle in the log viewer toolbar,\nalongside auto-refres\n[…]\noutweighs Chonky's rule\nafter Svelte scoping. The byte still reads distinctly via the danger\ncolour and semibold weight, just quietly inline.\n\n* Rename the non-printable toggle label to Non-ASCII data",
"is_bot": false,
"headline": "Add a Hex bytes toggle for non-printable bytes in the packet log (#388)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T23:14:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "d6eb8159e7af194f5246a862dd91cf9133c66ff5",
"body": "* Add auto-refresh and auto-scroll toggles to APRS Logs\n\nThe packet log polled and followed new packets unconditionally, so a full\nbuffer scrolled and rewrote rows under the operator while they were reading\na packet path -- worst on mobile where only a few rows are visible (#373).\n\nAdd two device-lo\n[…]\nen carries over. Auto-refresh here gates\nonly the packet feed -- the status cards and position keep polling -- so\nthe operator can freeze the log to read it without stalling the rest of\nthe dashboard.",
"is_bot": false,
"headline": "Add auto-refresh and auto-scroll toggles to APRS Logs (#386)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T22:11:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "71c0244e4b74dfeba5924f5e73ac7d4d138badd7",
"body": "* Surface Mic-E beacon comments (GH #377)\n\nparseMicE decoded the free-form comment into MicE.Status but never\ncopied it to pkt.Comment, which is the only field the stationcache and\nmobile UI read -- so mobile-beacon comments (e.g. 'KK4CUK Matt's Cozy')\ndecoded correctly yet rendered blank while aprs\n[…]\nced to assert the base-91 DAO wedged in\nthe telemetry tail is consumed and applied, not merely stripped, and\ndocument that a lone unterminated '|' is treated as a truncated\ntelemetry opener by design.",
"is_bot": false,
"headline": "Surface Mic-E mobile-beacon comments (GH #377) (#385)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T20:22:31Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b7b0b439592e358b8ee3ac66529ff97828e12edb",
"body": "* Surface non-printable bytes in the APRS packet log (GH #376)\n\nMalformed packets can carry control bytes that render invisibly in the\nlog line yet break decoding -- most famously a 0x7F (DEL) wedged into a\nposition beacon, which silently prevents map plotting. Render each\nnon-printable byte inline \n[…]\nas defensive. Give each\ntoken an accurate per-byte tooltip sourced from the segment, so a\nU+FFFD token reads \"invalid byte (replaced with U+FFFD in transit)\"\ninstead of implying a literal byte 0xFFFD.",
"is_bot": false,
"headline": "Surface non-printable bytes in the APRS packet log (GH #376) (#384)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T20:19:13Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7ac573f1e456d5f97e916c5aace03c72fb8e2de1",
"body": "The default light theme used a bright #ffaa00 for --color-primary, which\ndoubles as a foreground color (links, chonky-ui's outlined btn-primary at\nrest, message leads, status icons). On the light surfaces it sat on, that\nyellow-orange fell to ~1.7:1 contrast on the grey button background -- well\nbel\n[…]\n while clearing WCAG\nAA as text on every light surface (>=4.6:1) and under white fill text\n(5.3:1). graywolf-night is unaffected -- bright orange on its near-black\nbackground is already high-contrast.",
"is_bot": false,
"headline": "Improve graywolf default-theme primary contrast (#381)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T18:24:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "df5070908052f5d7cf6bf7a691e5e712251adeba",
"body": "* Explain how to test keying for wireless PTT methods\n\n\"Test PTT\" on a PTT card manually pulses the PTT wire, but VOX and Digirig\ntone PTT have no wire -- their modem PTT driver is a no-op, so the button\ndid nothing and was rendered permanently greyed. Operators read the dead\nbutton as a broken cont\n[…]\nflags (isToneKeyed, canTestPttWire) so the Test PTT\nbutton and Test TX hint can't drift out of sync when a method is added.\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Explain how to test keying for VOX / Digirig tone PTT (#383)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T17:48:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9ab8058221a4e6e0f380a8b52f2fe78b298ff95d",
"body": "* android: keep message compose bar above the soft keyboard\n\nOn Android 15+ (targetSdk 36) edge-to-edge is mandatory and the system no\nlonger resizes the window for the soft keyboard, so the Messages tab compose\nbar sat behind the on-screen keyboard. Handle window insets in MainActivity:\nopt into ed\n[…]\nchrome_bg, used by both the theme's\n statusBarColor and the WebView background.\n- Clarify the MainActivity comment that API 28-29 relies on adjustResize, not\n the inset path (Type.ime() is 0 there).",
"is_bot": false,
"headline": "android: keep message compose bar above the soft keyboard (#379)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T15:42:25Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0cfab51ab1dcc6cc7d2e434ba3a54d9cca001339",
"body": "Co-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Recommend Serial RTS PTT for Digirig Mobile (#378)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T15:18:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c8200250c52f6783e11acb0baaf856045df8d0db",
"body": "The Android \"Digirig Lite (tone PTT)\" method keys the radio via an audio\ntone and needs no serial/HID device, but needsDevice() only skipped the\ndevice-selection step for VOX (ptt_method 4), not tone PTT (ptt_method 5).\nSelecting it pushed the operator into the device picker, which lists no\ndevices \n[…]\nod with no deviceClass, leaving Save permanently\ndisabled so the method could never be applied.\n\nTreat ptt_method 5 like VOX and like desktop's digirig_tone: skip the\ndevice step and persist directly.",
"is_bot": false,
"headline": "Fix Digirig Lite tone PTT greyed out on Android",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-22T14:56:30Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8594e323ed5583bfcd7000bd309c233ec5e52aa7",
"body": "When the operator opens the live map while already offline, the basemap\nstyle fetch fails, so maplibre-map never fires oncreate, dataStore.start()\nnever runs, and pollingState stays at its initial 'idle' -- the status bar\nshowed a misleading green dot + 'idle'. PR #369's start() seed of\npollingState\n[…]\nfailures into the shared\nconnection store so the offline state is detected even when the map is\nthe first screen to touch the network.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Map status bar: show error dot when offline at mount (GH #374) (#375)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T20:12:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8437c641a0085301477dfcef1025894dc90ebb9d",
"body": null,
"is_bot": false,
"headline": "Release v0.14.7",
"author_name": "chrissnell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T16:16:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "aaf791841335561f20dd1239160b72daa926a8fa",
"body": "Co-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Move Mosiah quote between This install and Updates on About tab (#371)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T16:03:27Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "da56ff2af4a7c725dd2061d980f09945c9771c51",
"body": "Co-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Move APRS log locate icon to the left of the source callsign (#372)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T16:02:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bf6805ebf8e2c0d6eed75de29eff9219b6338d6f",
"body": "The pinned fork rev decoded the htstamp buffer assuming the time64\nlayout unconditionally, returning garbage on pre-t64 (time32) libasound\nand flooding cpal with 'get_htstamp was earlier than get_trigger_htstamp'\nerrors / stream rebuilds on Ubuntu 22.04 armv7l (#336). Bump to rev\n56099e8, whose decode is ABI-aware, so one time32 binary is correct on\nboth pre-t64 and t64 userlands. Updates Cargo.lock and documents the\ninverse regression in the design doc.",
"is_bot": false,
"headline": "modem: bump alsa-rs fork to ABI-aware htstamp decode (#336) (#370)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T15:12:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b392a9adc53780c6e85a2c31c33e9515a5c06cce",
"body": "* Show lost-connection indicator instead of fabricated data (#365)\n\nWhen the browser loses contact with the server, the REST client\n(lib/api.js) used to silently fall back to dev mock data on any network\nfailure, so the Dashboard and APRS Logs rendered fake channels, a fake\nposition, and fake beacon\n[…]\nows (incl. 401) are reachable\n and already marked connected.\n- Logs.svelte: replace the now-stale \"mock fallback\" comment.\n\n---------\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Show lost-connection indicator instead of fabricated data (#365) (#369)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T15:11:06Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "30b91c0923c8c24254dcc7baa0ccdd19fc5d7895",
"body": "The offline-map slug grammar reserves the bare slug `world` for the\nsingle global archive; every other slug is namespaced. The two startup\nmigrations that namespace legacy bare slugs predate the world archive and\nrewrote a downloaded `world` row+file to `state/world` on every restart.\nOffline render\n[…]\nrateLegacyArchives,\nand repair any already-mangled `state/world` row and file back to\n`world`. Adds tests and documents the invariant.\n\nCo-authored-by: chrissnell <chrissnell@users.noreply.github.com>",
"is_bot": false,
"headline": "Fix world map showing Download button after download (GH #364) (#368)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T14:32:58Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5ea9e55903f585001812a01d2585d26594a79058",
"body": "* Fix false 'clock not synchronized' warning on NTP-synced hosts\n\nThe startup clock-sync check classified on the adjtimex STA_UNSYNC status\nbit, which is an unreliable signal for whether a time source is\ndisciplining the clock. chrony leaves STA_UNSYNC set unless the rtcsync\ndirective is configured,\n[…]\nl status' across daemons and\nconfigurations, and matches the diag collector, which already reads\ntimedatectl's NTPSynchronized.\n\n* clocksync: express test out-of-range values relative to the threshold",
"is_bot": false,
"headline": "Fix false 'clock not synchronized' warning on NTP-synced hosts (#366)",
"author_name": "Chris Snell",
"author_login": "chrissnell",
"committed_at": "2026-06-20T14:28:23Z",
"body_truncated": true,
"is_coding_agent": false
}
],
"releases_count": 100,
"commits_last_year": 1925,
"latest_release_at": "2026-07-17T05:42:34Z",
"latest_release_tag": "v0.14.11",
"releases_from_tags": false,
"days_since_last_push": 4,
"active_weeks_last_year": 17,
"days_since_latest_release": 5,
"mean_days_between_releases": 3.3
},
"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/chrissnell/graywolf",
"exists": true,
"license": null,
"keywords": [],
"ecosystem": "go",
"matches_repo": true,
"registry_url": "https://pkg.go.dev/github.com/chrissnell/graywolf",
"is_deprecated": false,
"latest_version": "v0.14.11",
"repository_url": "https://github.com/chrissnell/graywolf",
"versions_count": 119,
"total_downloads": null,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": null,
"first_published_at": null,
"latest_published_at": "2026-07-17T05:34:54Z",
"latest_version_yanked": null,
"days_since_latest_publish": 5
}
]
},
"popularity": {
"forks": 19,
"stars": 177,
"watchers": 12,
"fork_history": {
"days": [
{
"date": "2026-04-19",
"count": 1
},
{
"date": "2026-04-23",
"count": 1
},
{
"date": "2026-04-25",
"count": 1
},
{
"date": "2026-04-27",
"count": 1
},
{
"date": "2026-05-01",
"count": 1
},
{
"date": "2026-05-04",
"count": 1
},
{
"date": "2026-05-06",
"count": 1
},
{
"date": "2026-05-10",
"count": 1
},
{
"date": "2026-05-19",
"count": 1
},
{
"date": "2026-05-21",
"count": 1
},
{
"date": "2026-06-05",
"count": 1
},
{
"date": "2026-06-06",
"count": 1
},
{
"date": "2026-06-11",
"count": 1
},
{
"date": "2026-06-14",
"count": 1
},
{
"date": "2026-06-15",
"count": 1
},
{
"date": "2026-06-22",
"count": 1
},
{
"date": "2026-07-10",
"count": 1
},
{
"date": "2026-07-11",
"count": 1
},
{
"date": "2026-07-22",
"count": 1
}
],
"complete": true,
"collected": 19,
"total_forks": 19
},
"star_history": null,
"open_issues_and_prs": 20
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [
"examples"
],
"has_llms_txt": false,
"has_dockerfile": true,
"has_mcp_signal": false,
"bootstrap_files": [
"Makefile"
],
"api_schema_files": [
"docs/handbook/openapi.json",
"docs/handbook/openapi.yaml",
"pkg/webapi/docs/gen/swagger.json",
"pkg/webapi/docs/gen/swagger.yaml",
"proto/graywolf.proto",
"proto/platform.proto"
],
"has_devcontainer": false,
"typecheck_configs": [
"web/src/api/tsconfig.json"
],
"toolchain_manifests": [
"Cargo.toml",
"android/app/build.gradle.kts",
"android/build.gradle.kts",
"go.mod",
"graywolf-modem/Cargo.toml"
],
"largest_source_bytes": 117468,
"source_files_sampled": 1099,
"oversized_source_files": 6,
"agent_instruction_files": [
"CLAUDE.md"
],
"agent_instruction_max_bytes": 4522
},
"dependencies": {
"manifests": [
"Cargo.toml",
"Gemfile",
"android/build.gradle.kts",
"go.mod",
"graywolf-modem/Cargo.toml",
"web/package.json"
],
"advisories": {
"error": null,
"scope": null,
"source": null,
"findings": [],
"collected": false,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 0,
"malicious_count": 0,
"assessed_package": null,
"unassessed_count": 0,
"direct_affected_count": 0
},
"ecosystems": [
"crates",
"go",
"maven",
"npm",
"rubygems"
],
"dependencies": [
{
"name": "fastlane",
"manifest": "Gemfile",
"ecosystem": "rubygems",
"version_constraint": null
},
{
"name": "github.com/coder/websocket",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.8.14"
},
{
"name": "github.com/glebarez/go-sqlite",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.21.2"
},
{
"name": "github.com/glebarez/sqlite",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.11.0"
},
{
"name": "github.com/invopop/jsonschema",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.14.0"
},
{
"name": "github.com/pquerna/otp",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.5.0"
},
{
"name": "github.com/prometheus/client_golang",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.23.2"
},
{
"name": "github.com/prometheus/client_model",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.6.2"
},
{
"name": "github.com/warthog618/go-gpiocdev",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.9.1"
},
{
"name": "go.bug.st/serial",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.6.4"
},
{
"name": "go.uber.org/goleak",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.3.0"
},
{
"name": "golang.org/x/crypto",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.49.0"
},
{
"name": "golang.org/x/sys",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v0.42.0"
},
{
"name": "google.golang.org/protobuf",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.36.11"
},
{
"name": "gopkg.in/yaml.v3",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v3.0.1"
},
{
"name": "gorm.io/gorm",
"manifest": "go.mod",
"ecosystem": "go",
"version_constraint": "v1.31.1"
},
{
"name": "chrono",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.4"
},
{
"name": "claxon",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.4"
},
{
"name": "hound",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "3"
},
{
"name": "prost",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.13"
},
{
"name": "bytes",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1"
},
{
"name": "cpal",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "0.17"
},
{
"name": "serde",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1"
},
{
"name": "serde_json",
"manifest": "graywolf-modem/Cargo.toml",
"ecosystem": "crates",
"version_constraint": "1"
},
{
"name": "@americana/maplibre-shield-generator",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.1.1"
},
{
"name": "@chrissnell/chonky-ui",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.4.2"
},
{
"name": "@internationalized/date",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^3.8.0"
},
{
"name": "@xterm/addon-search",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.16.0"
},
{
"name": "@xterm/addon-serialize",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.14.0"
},
{
"name": "@xterm/addon-web-links",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.12.0"
},
{
"name": "@xterm/xterm",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^6.0.0"
},
{
"name": "bits-ui",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^2.0.0"
},
{
"name": "lucide-svelte",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^0.577.0"
},
{
"name": "maplibre-gl",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^5.24.0"
},
{
"name": "openapi-fetch",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "0.17.0"
},
{
"name": "pmtiles",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^4.4.1"
},
{
"name": "qrcode",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^1.5.4"
},
{
"name": "svelte-spa-router",
"manifest": "web/package.json",
"ecosystem": "npm",
"version_constraint": "^4.0.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": 8,
"merged_prs": 296,
"open_issues": 12,
"closed_ratio": 0.931,
"closed_issues": 162,
"closed_unmerged_prs": 13
},
"bus_factor": 1,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "chrissnell",
"commits": 1907,
"avatar_url": "https://avatars.githubusercontent.com/u/1072626?v=4"
},
{
"type": "User",
"login": "kotfu",
"commits": 9,
"avatar_url": "https://avatars.githubusercontent.com/u/218536?v=4"
},
{
"type": "User",
"login": "fromalexx",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/1144391?v=4"
},
{
"type": "User",
"login": "SteveHawk",
"commits": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/22629232?v=4"
},
{
"type": "User",
"login": "taco40sauce",
"commits": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/80659081?v=4"
}
],
"contributors_sampled": 5,
"top_contributor_share": 0.992
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"android.yml",
"armhf-t64-htstamp.yml",
"ci.yml",
"fuzz.yml",
"release.yml"
],
"has_docs_dir": true,
"linter_configs": [],
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [
"Cargo.lock",
"go.sum",
"package-lock.json"
],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 9,
"reason": "binaries present in source code",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": 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": 9,
"reason": "14 out of 15 merged PRs checked by a CI test -- score normalized to 9",
"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": 1,
"reason": "Found 3/16 approved changesets -- score normalized to 1",
"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": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 10,
"reason": "project is 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 18 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": 10,
"reason": "packaging workflow detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 1,
"reason": "dependency not pinned by hash detected -- score normalized to 1",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 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": 0,
"reason": "33 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "6ac926a165d1ff61276be827d35dc162488cdda7",
"ran_at": "2026-07-23T01:15:13Z",
"aggregate_score": 4,
"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-22T10:17:12Z",
"oldest_open_prs": [
{
"number": 98,
"created_at": "2026-05-08T19:13:13Z",
"last_comment_at": "2026-05-10T21:33:22Z",
"last_comment_author": "SteveHawk"
},
{
"number": 191,
"created_at": "2026-05-24T16:57:51Z",
"last_comment_at": "2026-05-28T04:38:20Z",
"last_comment_author": "chrissnell"
},
{
"number": 192,
"created_at": "2026-05-27T04:01:00Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 345,
"created_at": "2026-06-19T03:22:52Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 444,
"created_at": "2026-07-02T13:38:03Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 449,
"created_at": "2026-07-04T02:55:50Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 487,
"created_at": "2026-07-18T18:01:12Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 491,
"created_at": "2026-07-22T19:05:40Z",
"last_comment_at": null,
"last_comment_author": null
}
],
"last_merged_pr_at": "2026-07-17T05:22:44Z",
"ci_last_conclusion": "SUCCESS",
"oldest_open_issues": [
{
"number": 53,
"created_at": "2026-05-02T03:36:56Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 143,
"created_at": "2026-05-17T01:51:31Z",
"last_comment_at": "2026-05-30T18:17:14Z",
"last_comment_author": "pflarue"
},
{
"number": 208,
"created_at": "2026-05-30T14:48:16Z",
"last_comment_at": "2026-06-16T23:18:22Z",
"last_comment_author": "ropucyka"
},
{
"number": 429,
"created_at": "2026-06-28T16:36:06Z",
"last_comment_at": "2026-07-06T18:28:45Z",
"last_comment_author": "MaineWebDad"
},
{
"number": 445,
"created_at": "2026-07-02T13:52:35Z",
"last_comment_at": "2026-07-06T21:04:47Z",
"last_comment_author": "chrissnell"
},
{
"number": 452,
"created_at": "2026-07-06T05:11:10Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 456,
"created_at": "2026-07-06T22:44:23Z",
"last_comment_at": "2026-07-18T18:43:04Z",
"last_comment_author": "chrissnell"
},
{
"number": 479,
"created_at": "2026-07-14T20:19:30Z",
"last_comment_at": "2026-07-17T14:06:23Z",
"last_comment_author": "ragincajun93"
},
{
"number": 482,
"created_at": "2026-07-15T15:09:40Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 488,
"created_at": "2026-07-22T18:14:06Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 489,
"created_at": "2026-07-22T18:14:07Z",
"last_comment_at": "2026-07-22T19:26:09Z",
"last_comment_author": "kotfu"
},
{
"number": 490,
"created_at": "2026-07-22T18:14:08Z",
"last_comment_at": null,
"last_comment_author": null
}
]
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/chrissnell/graywolf",
"host": "github.com",
"name": "graywolf",
"owner": "chrissnell"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 62,
"inputs": {
"security": 40,
"vitality": 82,
"community": 52,
"governance": 63,
"engineering": 66
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "good",
"name": "Vitality",
"value": 82,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 76,
"inputs": {
"commits_last_year": 1925,
"human_commit_share": 1,
"days_since_last_push": 4,
"active_weeks_last_year": 17
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 4 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "push_recency",
"params": {
"days": 4
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "17/52 weeks with commits",
"points": 11.8,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 17
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "1925 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 1925
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 18 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": 100,
"latest_release_tag": "v0.14.11",
"releases_from_tags": false,
"days_since_latest_release": 5,
"mean_days_between_releases": 3.3
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "100 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 100
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 5 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "release_recency",
"params": {
"days": 5
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~3.3 days",
"points": 27,
"status": "met",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 3.3
}
}
],
"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": "moderate",
"name": "Community & Adoption",
"value": 52,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "moderate",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 53,
"inputs": {
"forks": 19,
"stars": 177,
"watchers": 12,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "177 stars",
"points": 36.4,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 177
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "19 forks",
"points": 10.5,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 19
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "12 watchers",
"points": 5.8,
"status": "partial",
"details": [
{
"code": "watchers",
"params": {
"count": 12
}
}
],
"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 (GPL-2.0)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "GPL-2.0"
}
}
],
"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": 63,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "critical",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 22,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 5,
"top_contributor_share": 0.992
},
"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 99% of commits",
"points": 0.2,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 99
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "5 contributors",
"points": 6.8,
"status": "partial",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 5
}
}
],
"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": "good",
"name": "Issue & PR responsiveness",
"note": null,
"notes": [],
"value": 82,
"inputs": {
"merged_prs": 296,
"open_issues": 12,
"closed_issues": 162,
"issue_closed_ratio": 0.931,
"closed_unmerged_prs": 13
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "93% of issues closed",
"points": 43.5,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 93
}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "296/309 decided PRs merged",
"points": 36.6,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 296,
"decided": 309
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 3/16 approved changesets -- score normalized to 1",
"points": 1.5,
"status": "partial",
"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": 63,
"inputs": {
"followers": 129,
"owner_type": "User",
"is_verified": null,
"owner_login": "chrissnell",
"public_repos": 91,
"account_age_days": 5417
},
"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": "129 followers of chrissnell",
"points": 15.2,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 129,
"login": "chrissnell"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "91 public repos, account ~14 yr old",
"points": 25,
"status": "met",
"details": [
{
"code": "public_repos",
"params": {
"count": 91
}
},
{
"code": "account_age_years",
"params": {
"years": 14
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"github.com/chrissnell/graywolf"
],
"ecosystems": "go",
"any_deprecated": false,
"min_days_since_publish": 5
},
"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 5 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 5
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "119 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 119
}
}
],
"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": "moderate",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 66,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "5 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 5
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "14 out of 15 merged PRs checked by a CI test -- score normalized to 9",
"points": 18,
"status": "partial",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "moderate",
"name": "Documentation",
"note": null,
"notes": [],
"value": 65,
"inputs": {
"topics": [],
"has_wiki": false,
"homepage": null,
"has_readme": true,
"has_docs_dir": true,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 25,
"status": "met",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "at_risk",
"name": "Security",
"value": 40,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "at_risk",
"name": "Security posture",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 18,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 0,
"scorecard_aggregate": 4
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "binaries present in source code",
"points": 6.8,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "branch protection not enabled on development/release branches",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "14 out of 15 merged PRs checked by a CI test -- score normalized to 9",
"points": 2.2,
"status": "partial",
"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 3/16 approved changesets -- score normalized to 1",
"points": 0.8,
"status": "partial",
"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": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is fuzzed",
"points": 5,
"status": "met",
"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 18 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow detected",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 1",
"points": 0.5,
"status": "partial",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is 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": "33 existing vulnerabilities detected",
"points": 0,
"status": "missed",
"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": 3
},
"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": "good",
"name": "AI Readiness",
"value": 80,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "excellent",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 85,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.93,
"agent_instruction_files": [
"CLAUDE.md"
],
"agent_instruction_max_bytes": 4522
},
"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": "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": "good",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 70,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [
"Cargo.lock",
"go.sum",
"package-lock.json"
],
"has_dockerfile": true,
"typed_language": true,
"bootstrap_files": [
"Makefile"
],
"has_devcontainer": false,
"has_linter_config": false,
"typecheck_configs": [
"web/src/api/tsconfig.json"
],
"agent_commit_share": 0.04,
"toolchain_manifests": [
"Cargo.toml",
"android/app/build.gradle.kts",
"android/build.gradle.kts",
"go.mod",
"graywolf-modem/Cargo.toml"
],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": "Makefile",
"points": 18,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "Makefile"
}
}
],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": "web/src/api/tsconfig.json",
"points": 11,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "web/src/api/tsconfig.json"
}
}
],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": "Dockerfile, lockfile",
"points": 10,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "Dockerfile, lockfile"
}
}
],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "4 of the last 100 commits agent-authored or agent-credited",
"points": 8,
"status": "partial",
"details": [
{
"code": "agent_authored_commits",
"params": {
"count": 4,
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 1",
"points": 1,
"status": "partial",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "excellent",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"primary_language": "Go",
"largest_source_bytes": 117468,
"source_files_sampled": 1099,
"oversized_source_files": 6
},
"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": "6/1099 source files over 60KB",
"points": 54.7,
"status": "partial",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 1099,
"oversized": 6
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "good",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 80,
"inputs": {
"example_dirs": [
"examples"
],
"has_mcp_signal": false,
"api_schema_files": [
"docs/handbook/openapi.json",
"docs/handbook/openapi.yaml",
"pkg/webapi/docs/gen/swagger.json",
"pkg/webapi/docs/gen/swagger.yaml",
"proto/graywolf.proto",
"proto/platform.proto"
]
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": "docs/handbook/openapi.json, docs/handbook/openapi.yaml, pkg/webapi/docs/gen/swagger.json, pkg/webapi/docs/gen/swagger.yaml, proto/graywolf.proto, proto/platform.proto",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "docs/handbook/openapi.json, docs/handbook/openapi.yaml, pkg/webapi/docs/gen/swagger.json, pkg/webapi/docs/gen/swagger.yaml, proto/graywolf.proto, proto/platform.proto"
}
}
],
"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": "examples",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "examples"
}
}
],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
"Could not fetch crates package 'graywolf-demod' from its registry",
"GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
],
"report_type": "repository",
"generated_at": "2026-07-23T01:15:32.333452Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/chrissnell/graywolf.svg",
"full_name": "chrissnell/graywolf",
"license_state": "standard",
"license_spdx": "GPL-2.0"
}