Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-24 21:36 UTC

iRootPro / lofi-player

A small TUI for lofi internet radio. Streams, YouTube live, ambient sounds

GoMIT★ 1 estrella⑂ 0 forksdesde abr 2026Ver en GitHub ↗

iRootPro/lofi-player tiene un índice de salud de 42 sobre 100, lo que lo sitúa en la banda En riesgo. Su puntuación más alta es Vitality (60/100) y la más baja, Community & Adoption (24/100). Se actualizó por última vez hace 36 días. Una sola persona concentra la mayor parte del trabajo reciente.

42
global / 100
En riesgo

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala de 1 a 100. El resultado global parte de su media; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe el límite 49 (En riesgo). Preparación para IA queda fuera.

42
Excelente85-100Ejemplar; cumple prácticamente todos los criterios evaluados
Bueno70-84Saludable; carencias menores
Moderado50-69Aceptable con carencias notables; se recomienda revisión
En riesgo30-49Debilidades significativas; su adopción exige cautela
Crítico1-29Problemas graves (proyecto abandonado, un solo mantenedor, sin higiene)
VitalidadComunidad yAdopciónSostenibilidady GobernanzaCalidad deIngenieríaSeguridadPreparaciónpara IA

Perfil de puntuación

Cada eje es una categoría. La forma importa más que la media: un proyecto sano llena toda la figura, mientras que un perfil de picos y cráteres indica que la fortaleza en una dimensión enmascara el riesgo en otra.

Titularidad

iRootProCuenta personal
0 seguidores81 repositorios públicosdesde may 2016

Este repositorio pertenece a una cuenta personal. Un proyecto con un único propietario conlleva más riesgo de continuidad que uno respaldado por una organización.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicación
Gogithub.com/iRootPro/lofi-playerv0.1.12-13hace 36 días

Métricas por categoría

Vitalidad

¿Está vivo el proyecto: se escribe código y se publican versiones?

60Moderado · 22% del índice global
Cómo se puntúa
18/36Recencia de push — último push hace 36 días
3.5/36Cadencia de commits — 5/52 semanas con commits
18/18Volumen de commits — 101 commits en el último año
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Datos de entrada utilizados
commits_last_year101
human_commit_share1
days_since_last_push36
active_weeks_last_year5
Cómo se puntúa
27/27Publica versiones — 13 versiones publicadas
36/36Recencia de las versiones — última versión hace 36 días
27/27Cadencia de publicación — una versión cada ~5,1 días
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Datos de entrada utilizados
releases_count13
latest_release_tagv0.1.12
releases_from_tagsno
days_since_latest_release36
mean_days_between_releases5,1

Comunidad y Adopción

¿Tiene el proyecto usuarios, descargas, atención y unas condiciones acogedoras para quienes contribuyen?

24Crítico · 18% del índice global
Cómo se puntúa
0/60Estrellas — 1 estrellas
0/25Forks — 0 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks0
stars1
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (MIT)
0/18Guía CONTRIBUTING
0/13.5Código de conducta
0/7.2Plantilla de issues
0/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno

Sostenibilidad y Gobernanza

¿Sobrevivirá el proyecto a sus personas: factor bus, capacidad de respuesta, quién lo respalda y mantenimiento del paquete?

34En riesgo · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
0/46.8Resolución de issues — sin issues o sin datos
0/38.3Aceptación de PR — sin PR decididos o sin datos
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs0
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues, Aceptación de PR. Los pesos restantes se han renormalizado.
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
0/25Alcance del propietario — 0 seguidores de iRootPro
25/25Trayectoria — 81 repos públicos, cuenta de ~10 años
Datos de entrada utilizados
followers0
owner_typeUser
is_verified
owner_loginiRootPro
public_repos81
account_age_days3734
Excluidos de la puntuación (sin datos o no aplicable): Dominio verificado. Los pesos restantes se han renormalizado.
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en go
35/35Recencia de publicación — última publicación hace 36 días
20/20Historial de versiones — 13 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesgithub.com/iRootPro/lofi-player
ecosystemsgo
any_deprecatedno
min_days_since_publish36

Calidad de Ingeniería

¿Existen unas prácticas mínimas de ingeniería y documentación?

56Moderado · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 1 flujo(s) de trabajo
24/24Pruebas presentes
0/16Configuración de linter
0/9.6Hooks de pre-commit
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — sin datos
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_configno
has_precommit_configno
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: CI-Tests. Los pesos restantes se han renormalizado.

Documentación

50Moderado
Cómo se puntúa
30/30README
0/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
0/10Topics
10/10Wiki
Datos de entrada utilizados
topics
has_wiki
homepage
has_readme
has_docs_dirno
has_description

Seguridad

¿Son sólidas las prácticas visibles de seguridad y de cadena de suministro, sin exposición jurisdiccional de alto riesgo sin resolver?

34En riesgo · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — sin datos
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Licencia — license file detected
0/7.5Maintained — project was created within the last 90 days. Please review its contents carefully
0/5Packaging — sin datos
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
7.5/7.5Token-Permissions — GitHub workflow tokens follow principle of least privilege
6/7.5Vulnerabilities — 2 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3,4
Excluidos de la puntuación (sin datos o no aplicable): ci_tests, packaging. Los pesos restantes se han renormalizado.

Preparación para IA

¿Hasta qué punto está el repositorio preparado para desarrollarse y mantenerse con agentes de codificación de IA? Es una insignia independiente y experimental — peso 0,0, de modo que se presenta por separado y no afecta a la puntuación de salud global.

58Moderado · 0% del índice global
Cómo se puntúa
0/45Instrucciones para agentes — sin CLAUDE.md / AGENTS.md / reglas de editor
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 97 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,97
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
12.6/18Arranque con un solo comando — go.mod (convención del toolchain, sin ejecutor de tareas)
22/22Pruebas automatizadas
0/11Configuración de lint / formato
11/11Verificación estática de tipos — Go (tipado estático)
10/10Entorno reproducible — lockfile
0/10Práctica demostrada con agentes — ningún commit con autoría de agente entre los últimos 100
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Datos de entrada utilizados
has_nixno
has_tests
lockfilesgo.sum
has_dockerfileno
typed_language
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configs
agent_commit_share0
toolchain_manifestsgo.mod
dependency_bot_commit_share0
Cómo se puntúa
45/45Código verificable por tipos — Go (tipado estático)
55/55Tamaños de archivo manejables — 0/37 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languageGo
largest_source_bytes34.574
source_files_sampled37
oversized_source_files0

Datos clave

1estrellas de GitHub
1contribuidores
101commits en los últimos 12 meses
36días desde el último push
13versiones publicadas
1factor bus
0issues abiertas
Goecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Más detalle

OpenSSF Scorecard 3.4 / 10
3.4agregado

Evaluación de seguridad independiente y agnóstica en cuanto a herramientas, procedente del proyecto de código abierto OpenSSF Scorecard. Cada comprobación premia una práctica de seguridad, no la herramienta de un proveedor concreto. Las comprobaciones que Scorecard no pudo determinar se marcan como n/d y se excluyen de la puntuación de seguridad (nunca se cuentan como cero).Scorecard v5.5.0 · 2026-07-24 21:36 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/dCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
0Maintainedproject was created within the last 90 days. Please review its contents carefully
n/dPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
10Token-PermissionsGitHub workflow tokens follow principle of least privilege
8Vulnerabilities2 existing vulnerabilities detected
Dependencias directas 5
RegistroPaqueteRestricción de versiónManifiesto
Gogithub.com/atotto/clipboardv0.1.4go.mod
Gogithub.com/charmbracelet/bubblesv1.0.0go.mod
Gogithub.com/charmbracelet/bubbleteav1.3.10go.mod
Gogithub.com/charmbracelet/lipglossv1.1.0go.mod
Gogopkg.in/yaml.v3v3.0.1go.mod
Todas las dependencias no recopilado

No fue posible recopilar el conjunto de dependencias resuelto para este informe: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 14018,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "Go": 251223,
        "HTML": 10875,
        "Shell": 3111,
        "Python": 4709
      },
      "pushed_at": "2026-06-18T09:43:03Z",
      "created_at": "2026-04-27T17:48:55Z",
      "owner_type": "User",
      "updated_at": "2026-06-18T09:42:58Z",
      "description": "A small TUI for lofi internet radio. Streams, YouTube live, ambient sounds",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Go",
      "significant_languages": [
        "Go"
      ]
    },
    "owner": {
      "blog": null,
      "name": null,
      "type": "User",
      "login": "iRootPro",
      "company": null,
      "location": null,
      "followers": 0,
      "avatar_url": "https://avatars.githubusercontent.com/u/19166457?v=4",
      "created_at": "2016-05-03T09:54:29Z",
      "is_verified": null,
      "public_repos": 81,
      "account_age_days": 3734
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.1.12",
          "kind": "patch",
          "published_at": "2026-06-18T09:43:49Z"
        },
        {
          "tag": "v0.1.11",
          "kind": "patch",
          "published_at": "2026-05-20T11:09:41Z"
        },
        {
          "tag": "v0.1.10",
          "kind": "patch",
          "published_at": "2026-05-20T10:09:55Z"
        },
        {
          "tag": "v0.1.9",
          "kind": "patch",
          "published_at": "2026-05-15T06:08:59Z"
        },
        {
          "tag": "v0.1.8",
          "kind": "patch",
          "published_at": "2026-05-13T11:14:42Z"
        },
        {
          "tag": "v0.1.7",
          "kind": "patch",
          "published_at": "2026-05-13T11:00:27Z"
        },
        {
          "tag": "v0.1.6",
          "kind": "patch",
          "published_at": "2026-05-13T10:02:43Z"
        },
        {
          "tag": "v0.1.5",
          "kind": "patch",
          "published_at": "2026-05-13T08:36:08Z"
        },
        {
          "tag": "v0.1.4",
          "kind": "patch",
          "published_at": "2026-05-12T13:16:38Z"
        },
        {
          "tag": "v0.1.3",
          "kind": "patch",
          "published_at": "2026-05-03T19:00:56Z"
        },
        {
          "tag": "v0.1.2",
          "kind": "patch",
          "published_at": "2026-05-02T19:23:30Z"
        },
        {
          "tag": "v0.1.1",
          "kind": "patch",
          "published_at": "2026-04-27T18:13:01Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-04-27T17:53:54Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "9a340a77e7668a24327a26d62771287328ed94ac",
          "body": null,
          "is_bot": false,
          "headline": "fix: persist theme selection immediately",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-06-18T09:42:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7f80415bf39267f0b1a96495f99720361c4086df",
          "body": null,
          "is_bot": false,
          "headline": "fix: cap initial buffer for live radio",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-20T11:09:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "956fb3d3c7c32c59e619abf2f66e1746e90c3c22",
          "body": null,
          "is_bot": false,
          "headline": "feat: add network buffer settings",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-20T10:09:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2df9f8443747e1ebe9dfcb7e6e855ce8df11e913",
          "body": null,
          "is_bot": false,
          "headline": "fix: reconnect stalled radio streams",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-15T06:07:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4ce486b4f1f6f10b0f6fed5d1a98ece2ba0dd20a",
          "body": null,
          "is_bot": false,
          "headline": "feat: add light themes",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-13T11:14:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da085b7e023a027f8fc2632ee0d6e7ebcc1bc5f7",
          "body": null,
          "is_bot": false,
          "headline": "feat: add theme picker",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-13T10:59:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9813ed6b6c31407eb55e3ad1a0deefec62574c96",
          "body": null,
          "is_bot": false,
          "headline": "feat: add station sharing",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-13T10:01:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "73dce8a64bd8dd1b4148a7a86aa6d5508b3c5953",
          "body": null,
          "is_bot": false,
          "headline": "audio: support macOS play pause media key",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-13T08:35:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c76ce91cde65d6bc6de28ce78db786292c36515d",
          "body": null,
          "is_bot": false,
          "headline": "fix: isolate mpv startup from user config",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-12T12:09:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "62692c179cddb7485a6beda2ee1f020be66a60af",
          "body": null,
          "is_bot": false,
          "headline": "Update README demo GIF",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-04T11:22:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55d8a0820d7de6edccd84232f227a288b54bf45f",
          "body": null,
          "is_bot": false,
          "headline": "docs: add PNG demo preview",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-03T19:16:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ded0f0d33ed20e64731350a6a28ce7b9e9f3dc72",
          "body": null,
          "is_bot": false,
          "headline": "fix: keep mpv IPC responsive under event bursts",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-03T18:59:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6ff0f7f31cc4432ce83fb4afcf2e76f276403c52",
          "body": null,
          "is_bot": false,
          "headline": "docs: update pinned install version",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-02T19:22:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "770d734aab03838c3e192d81a1dab7168112ad19",
          "body": null,
          "is_bot": false,
          "headline": "fix: polish TUI layout and modal overlays",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-05-02T19:21:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d6161dd8a0db0b5f4b6accf52fcf37c344e01d75",
          "body": null,
          "is_bot": false,
          "headline": "docs: refresh demo gif",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T19:26:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0ec712733b0acdd1f984e9661565ffc9a5aa67b4",
          "body": "Reorders the install section so Homebrew (which auto-installs mpv\nand yt-dlp) is clearly the path of least resistance. Adds a note to\nthe one-line installer pointing at the runtime-deps table since\nnon-brew installs need mpv installed separately. Lifts the supported\nplatforms line to the top of the section.",
          "is_bot": false,
          "headline": "docs: highlight Homebrew as the recommended install path",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T18:17:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8bd081d803f39b1eec320fcb114376ac41aa33c2",
          "body": null,
          "is_bot": false,
          "headline": "chore: ignore local /sounds/ scratch dir",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T18:12:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ed55394d704d52493ff3e892f60dff86e1be0a52",
          "body": "Releases are now cut locally with `goreleaser release --clean` rather\nthan from a CI workflow, mirroring the iRootPro/rdr setup. Goreleaser\nuploads binaries to GitHub Releases and pushes the formula to\niRootPro/homebrew-tap in one shot, using the local gh-cli token —\nno PAT secret needed in the repo\n[…]\nl/bin\n  (override with INSTALL_DIR=...). Hints when mpv is missing.\n- README.md / README.ru.md: install section adds Homebrew tap and\n  one-line installer paths; release notes describe the local flow.",
          "is_bot": false,
          "headline": "chore(release): switch to local release flow with Homebrew tap",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T18:10:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9764af8574cd6d05f7097032c13964d0f9258b4d",
          "body": "- README.md: language switcher in the header pointing at README.ru.md;\n  Features list mentions the friendly-first-run behavior; runtime\n  dependencies section now distinguishes the mpv hard-fail with the\n  styled card from the yt-dlp soft warning that keeps the rest of\n  the app playable.\n- README.ru.md: full Russian translation of the README, same\n  structure and links so the cross-references stay symmetric.",
          "is_bot": false,
          "headline": "docs: add Russian README and document graceful dependency handling",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T17:47:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "53173c5b348a40d2f26d80e2c88c2ee297238809",
          "body": "mpv missing on $PATH no longer crashes with a raw error. Instead a\nstyled `lofi.player can't start` card is rendered to stderr with\nplatform-specific install commands (brew / apt / pacman / dnf), and\nthe process exits 1.\n\nyt-dlp missing degrades to a soft warning rather than refusing to\nstart when t\n[…]\nause replaces the playCmd with an\n  error toast that names the missing tool.\n- The station list paints YouTube rows in muted hint color and\n  appends an `unavailable` badge so the user sees the cause.",
          "is_bot": false,
          "headline": "feat: friendly handling of missing runtime dependencies",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T17:46:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f410efa5076ef3612edfb75f8875c4d0d91e6450",
          "body": "Rewrites the README around the public-release shape:\n\n- Badges (CI, latest release, Go version, MIT)\n- Centered demo GIF (demo/lofi-player.gif from the vhs tape)\n- Features bullet list (radio, YouTube, ambient mixer, themes,\n  mini mode, statusline, persistence, edit/add/delete stations,\n  stream in\n[…]\nnd auto-detect note\n- Themes / mini mode / tmux statusline sections\n- Credits for CC0 ambient samples (rain, fire, white noise, cafe,\n  thunder) with original freesound.org links\n- MIT license callout",
          "is_bot": false,
          "headline": "docs: rewrite README with badges, demo, full keybindings",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T12:01:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5318e95fc81a0ebf4a88d7c7ce27d22f340e4751",
          "body": "UX polish:\n- Animation tick chains hoisted above modal dispatch so the logo\n  shimmer and live-pulse keep ticking when mixer/add-station modals\n  are open (the modal updaters only know about KeyMsg and were\n  silently swallowing tea.Tick chains, killing them forever).\n- Full-help card switched from \n[…]\nens a small `delete <name>?` confirmation modal centered in\n  the frame; `y/enter` commits, `n/esc` cancels. Deleting the\n  currently-playing station pauses playback and clears the\n  now-playing card.",
          "is_bot": false,
          "headline": "feat(tui): UX polish, stream info, edit/delete stations",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T12:01:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "95e7d8408caa7d24f3dcc7782522957c2bdcce1e",
          "body": "Adds the bits needed to publish the project to GitHub:\n\n- LICENSE (MIT, 2026 Alexander Neupokoev)\n- .goreleaser.yaml — builds linux/{amd64,arm64} + darwin/{amd64,arm64},\n  archives include README and LICENSE, checksums, ldflags inject\n  main.version\n- .github/workflows/ci.yml — vet + test + build on\n[…]\ne — /dist/ for goreleaser output\n- main.go — `--version`/`-v` flag, ldflags-overridable `version`\n  variable, ShowStreamInfo wiring through state\n- demo/ — vhs tape and pre-rendered GIF for the README",
          "is_bot": false,
          "headline": "chore(release): packaging artifacts and version flag",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-27T12:00:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cc8d1c398625c02b0f4efc99b9a4e497fc41c67d",
          "body": "Two new CC0 ambient channels appended to the registry, both sourced\nfrom freesound.org and processed via ffmpeg into 4-minute Opus loops:\n  - cafe.opus    ← freesound 32910 by ToddBradley (CC0)\n  - thunder.opus ← freesound 717890 by TRP (CC0)\n\nTotal ambient set: rain, fire, white_noise, cafe, thunde\n[…]\nover the expanded 5-channel registry — channel order\nwalk-through, label/icon metadata, and indicator rendering all\nparametrized off ChannelIDs() rather than hard-coded against the\noriginal 3 entries.",
          "is_bot": false,
          "headline": "Add cafe and thunder ambient channels; restyle mixer icons",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T18:41:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "316f5be4f4cdec7b75b84f42a7f13747ebc5b071",
          "body": "Adds the shimmering \"lofi\" ASCII logo on the right side of the\nmain view, vertically centered against the body height. Resolved\ntrivial conflicts where ambient-mixer and the logo each appended\nnew tick messages, tea.Cmd factories, and view helpers — kept\nboth. Bumped logo_test.go's NewModel call to match the new mixer\nparameter introduced on main.",
          "is_bot": false,
          "headline": "Merge branch 'feat/equalizer'",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T18:18:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "043e1b784dd2ffb6327de87a5a0c571d17bb01ee",
          "body": "Trim the spacer between the ♪ prefix and the L from 2 cells to 1\nso the music note reads as part of the word rather than floating\nbeside it. Swap the i's middle-dot (•) for a black-circle (●) so\nthe i carries comparable visual weight to the heavy-stroked L, O,\nand F instead of trailing off into a pin-prick.",
          "is_bot": false,
          "headline": "TUI: tighten logo prefix gap and beef up the i's dot",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T18:10:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "629b6aa0ee69d043db644d6ec693ebf08e206041",
          "body": "Move the shimmering logo from the top of the right column to its\nvertical middle. JoinHorizontal with lipgloss.Center pads the logo\nabove and below to match the full body height (now-playing card +\nstations list), so the right side reads as balanced rather than\ntop-heavy with empty space below the original 3-row logo strip.",
          "is_bot": false,
          "headline": "TUI: vertically center logo against body height",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T18:07:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0f205a09b61ac6d4d0bb10e69c146e915803bf10",
          "body": "Swap the light box-drawing glyphs (│─╭╮╰╯├) for their heavy\ncounterparts (┃━┏┓┗┛┣) so the letters carry more visual weight and\nthe shimmer wave reads more strongly across them. Bump the i's dot\nfrom · (middle dot) to • (bullet) to match the heavier strokes.",
          "is_bot": false,
          "headline": "TUI: thicken logo strokes with heavy box-drawing",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T18:03:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1a86fd53eb134fbacbca58cfab7b3a2a825bc9b9",
          "body": "Dot the \"i\" with a middle-dot glyph so the rightmost letter reads\nas a real lowercase \"i\" rather than a bare divider. Echo the music\nnote from the frame's top-border title on the middle row of the\nlogo, so the brand element lives in both the frame chrome and the\nlarger panel — same glyph, two scales.",
          "is_bot": false,
          "headline": "TUI: refine logo with dot-i and ♪ prefix",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T17:57:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "29ae60ffe5b8486772a026ba959cb271a6cabdbb",
          "body": "The vertical-bar equalizer felt too \"club\" for a lofi player; swap\nit for a small static \"lofi\" logo built from rounded box-drawing\ncharacters. A soft shimmer crest sweeps across the logo at 150 ms\nper cell, brightening cells from Muted → Secondary → Primary as the\nwave passes, with a brief dim \"res\n[…]\nanimation reads as a calm breath rather than a continuous spinner.\n\nShimmer freezes on pause and the logo collapses entirely when no\nstation is selected, matching the previous equalizer's state rules.",
          "is_bot": false,
          "headline": "TUI: replace equalizer with shimmering ASCII logo",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T17:47:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a9b7043ff40ce1518566481d00c34914d478cd6d",
          "body": "Adds the ambient mixer feature (Phase 4c from plans/lofi-player-plan.md):\n3-channel mixer (rain/fire/white_noise) layered over the main station,\nopened with 'x', state persisted via debounced save, embedded CC0 loops\nfrom freesound.org. See plans/2026-04-25-ambient-mixer-design.md for the\ndesign and plans/2026-04-25-ambient-mixer-plan.md for the implementation\nplan.",
          "is_bot": false,
          "headline": "Merge branch 'ambient-mixer'",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T17:44:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "10104624fbcf9f20b1730151331a8ea73399f55a",
          "body": "- rain.opus  ← freesound 525046 by speakwithanimals (CC0)\n- fire.opus  ← freesound 760474 by True_Killian (CC0)\n- white_noise.opus ← freesound 132275 by assett1 (CC0)\n\nAll three are public-domain field recordings. Encoded to Opus 64 kbps\nstereo with 2/3-second fade in/out and EBU R128 loudness norma\n[…]\nization\nto -23 LUFS. README documents the regen recipe so future contributors\ncan reproduce or swap the embeds without spelunking git history.\n\nTotal embed footprint ~4.3 MB (was ~4.2 MB synthesized).",
          "is_bot": false,
          "headline": "audio: replace synthesized loops with CC0 freesound recordings",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T17:42:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3e790501edf5e6f26d17b6942780892e8f25560a",
          "body": "The previous placeholders were 1-3 seconds of literal silence — fine for\nunit tests, useless for verifying the audio pipeline by ear. Swap them\nfor 4-minute Opus loops generated via ffmpeg's anoisesrc:\n\n- rain.opus  — pink noise filtered (highpass 500 / lowpass 7000) with\n  light tremolo to feel lik\n[…]\nd recordings — fine for\nshipping a first release (similar to what mynoise.net and Brain.fm\nship), with real CC-BY recordings as a follow-up before v1.0. Recipe\nand licensing live in the assets README.",
          "is_bot": false,
          "headline": "audio: replace silent placeholders with synthesized noise textures",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-26T17:24:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "79731cbaae5d5fc7171fca2efc8652015921a67b",
          "body": "- Init the mixer at startup (non-fatal on failure: the modal still\n  opens, channels show as 'unavailable').\n- Restore last-session ambient volumes from state.json before the\n  Bubble Tea program starts so playback resumes immediately.\n- Plumb a SaveAmbient closure that load-modify-saves the state file\n  on each debounced tick. Errors flow back to the model as toasts.\n- On clean shutdown, snapshot Volumes() into the final state.Save.",
          "is_bot": false,
          "headline": "main: wire ambient mixer init, restore, and persistence",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T20:05:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "11bafeaecd54d0f0bd6b1fe72c374f1791e4f517",
          "body": "SaveAmbient callback returns error now; the debounce handler renders an\nerror toast on failure, matching how config.Save errors flow through\nupdateAddStation. Tighten the nil-callback test (toast remains nil, seq\nunchanged) and add a wiring assertion that mixer keys return a non-nil\ntea.Cmd.",
          "is_bot": false,
          "headline": "tui: surface ambient save errors as toast (matches addform pattern)",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T20:05:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "36a388d9797a462fb74d02bc7af706fc3662c1ef",
          "body": "Sixteen vertical bars sit right of the now-playing block, animated\non a 120 ms tick via per-bar sin-driven phases. Bars freeze on pause,\nquiet to a small ripple while loading, and disappear when no station\nis selected or when the terminal is too narrow. The animation has no\naudio data behind it — the mpv JSON-IPC backend never surfaces PCM —\nso phases are seeded deterministically and snapshot tests are stable.",
          "is_bot": false,
          "headline": "TUI: animate decorative equalizer next to now-playing card",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T20:02:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "11f6689c402e66eaf4fcb8af5d961e96c6752b69",
          "body": null,
          "is_bot": false,
          "headline": "tui: debounce ambient state save by 500ms",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T20:01:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6d4e49190d1939ee46697af39c89fa9d66740307",
          "body": "Adds a compact 'mid-dot + icons' indicator to the station line in\nrenderNowPlaying. Pulls active channel ids from the audio mixer in\ncanonical order; hides entirely when nothing is active.",
          "is_bot": false,
          "headline": "tui: show active ambient channels next to station",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:57:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fd734c7ed39b7bf1c1da211f5e4c800d0045f062",
          "body": null,
          "is_bot": false,
          "headline": "tui: regression test for inert global keys in mixer modal",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:56:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "566936b46f6791416cdd2d578355dbfc3f976f63",
          "body": "Centered rounded card with 3 channel rows (icon, label, bar, value),\ntitle and hint bar. Selected row highlighted; muted (volume==0) rows\ndim; disabled rows show 'unavailable'.",
          "is_bot": false,
          "headline": "tui: render ambient mixer modal card",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:55:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14a1f92c2e9ccc98a80ab1bf5502afef9a309def",
          "body": "Wire the modal's nav and volume keys: j/k pick a channel, h/l adjust by\n±5, H/L by ±25, 0 mutes the active channel, 1 maxes it. Volume changes\ngo straight to the audio mixer's SetVolume; the modal model just tracks\nwhich row is selected.\n\nAdds audio.SetCacheDirForTest so cross-package tests can redirect the\nambient cache root without depending on $XDG_CACHE_HOME (which is\nignored by os.UserCacheDir on darwin).",
          "is_bot": false,
          "headline": "tui: ambient mixer keybindings (jk/hl/HL/0/1)",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:54:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea70f41d6acd8a8fb591da9a936c131a340cfdd4",
          "body": null,
          "is_bot": false,
          "headline": "tui: introduce mixerModel.Selected for modal state",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:53:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "951267d23994d9959ba10d2555196a6d12fde48d",
          "body": "- Adds modeMixer to viewMode plus a mixer field on Model and a\n  mixerModel struct in mixer.go (selection state only; volumes\n  live on audio.AmbientMixer).\n- NewModel now takes *audio.AmbientMixer; main.go constructs one\n  via audio.NewAmbientMixer (Init/Close wired up in Task 15).\n- Update routes input to updateMixer while modeMixer is active;\n  for now it only handles esc/x to close, plus q/ctrl+c. Full\n  navigation/volume keys land in Task 10.",
          "is_bot": false,
          "headline": "tui: add modeMixer with open/close transitions",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:52:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8dd6047b8e2ce56639e5f557e0888dace96b8a9a",
          "body": null,
          "is_bot": false,
          "headline": "tui: bind 'x' to open ambient mixer",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:49:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6946f08f5084328faa04ca313695954849cab546",
          "body": null,
          "is_bot": false,
          "headline": "state: persist ambient channel volumes",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:48:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f71b2172b8cd268cbd805acc639389c43c022439",
          "body": null,
          "is_bot": false,
          "headline": "audio: ambient mixer Volumes and ActiveIDs snapshots",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:48:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6313665eecc4235a6d4faad59747dc7d3df9a75e",
          "body": null,
          "is_bot": false,
          "headline": "audio: ambient mixer set-volume with pause-on-zero",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:45:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b863ecf58c83f81f3ab18cc2e939cd1c14300e22",
          "body": null,
          "is_bot": false,
          "headline": "audio: close orphaned mpv on Init failure; document placeholder regen",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:43:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8ebff4b04f19e14fe30b9be5548a24120a569db8",
          "body": "scripts/fetch-radiopotok.py scrapes chillout/jazz/classical/trance\ncategories from radiopotok.ru, deduplicates by URL+name, prefers\nmp3 > aac > other > m3u8, and emits configs/radiopotok.yaml.\nYouTube-only stations are converted to youtube-kind entries.\n\nOutput schema matches internal/config.Station so users can copy\nentries straight into ~/.config/lofi-player/config.yaml. Defaults\nin code are unchanged.",
          "is_bot": false,
          "headline": "configs: bundle ~200-station radiopotok.ru preset",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:42:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b62baded926d4c9369669be2d53ea05fb73ee869",
          "body": "Adds ambientPlayer wrapping per-channel mpv subprocess (loop=inf, idle=no,\nvolume=0, pause=yes). Init spawns one per channel; failure marks the\nchannel disabled and continues. Close terminates all subprocesses in\nparallel.\n\nTwo pragmatic fixes alongside the new code:\n- ambient_player cleanup no long\n[…]\ne text placeholders in ambient_assets/ with valid\n  silent Opus loops (1/2/3-second durations for distinct SHA-256). mpv\n  can decode these, so spawn tests don't depend on its decode-failure\n  timing.",
          "is_bot": false,
          "headline": "audio: spawn mpv per ambient channel on init",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:39:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "05dc2125c00e55486076ba9cb359c8640ba26100",
          "body": null,
          "is_bot": false,
          "headline": "audio: fsync materialize and wrap error contexts",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:24:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8cdf6e7ebf33aa4f9fe57de41f15a1f179b1a6a2",
          "body": null,
          "is_bot": false,
          "headline": "audio: embed ambient loop files and extract on init",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:19:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9a43b86bea1a95d5a967be92e4892e638ac9945c",
          "body": null,
          "is_bot": false,
          "headline": "audio: scaffold ambient channel registry",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:14:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1435e270f6a8bafb1443cff1cce14f3aaa395bae",
          "body": null,
          "is_bot": false,
          "headline": "plans: add ambient mixer implementation plan",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:10:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "410b3125ee214dde3acc0eb4ca4b2c8b1984014e",
          "body": null,
          "is_bot": false,
          "headline": "gitignore: add .worktrees/ for isolated feature workspaces",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:03:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "27e0a27e3f3133bba975c307187fcba1c6fd56ce",
          "body": null,
          "is_bot": false,
          "headline": "plans: add ambient mixer v1 design",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T19:00:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "348859348cb1fa307728576fa3848136b548f303",
          "body": "Two cells of whitespace between an icon and its label looked like a\ngap; one cell reads as a properly-paired tag. Apply to:\n\n  - title bar logo: \"♪  lofi.player\" → \"♪ lofi.player\"\n  - stations section header: \"  stations\" → \" stations\"\n\nVisually balances against the volume widget in the title bar (which\nalready used a single space) and tightens the leading icons against\ntheir text without losing breathing room.",
          "is_bot": false,
          "headline": "TUI: tighten icon-to-label spacing in logo and stations header",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:38:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "63eb623dd5e2ff43ffe4abe6906755180409f826",
          "body": "The previous Warning-yellow \"?\" stood out as a stranger in the\notherwise muted bottom border. Use AppTitle (Primary purple)\ninstead, matching the logo glyph at top-left and the volume icon\nat top-right. The three \"interactive entry points\" are now\nvisually consistent — same color says \"these belong together\".\n\n\"help\" word stays in HelpDesc muted so the hint as a whole still\nreads quietly against the border.",
          "is_bot": false,
          "headline": "TUI: paint \"?\" hint in the brand Primary color",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:35:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e6d5eda58aed4c2f8318afb24da9174d7f075105",
          "body": "The persistent row of bindings under the stations list was visual\nchatter for users who already know the keys. Replace it with a\nsingle muted \"? help\" hint embedded in the bottom border (mirroring\nthe volume widget that lives in the top border):\n\n  ╭─ ♪ lofi.player ─────────  󰕿 ▰▰▰▰▰▰▱▱▱▱▱▱▱▱ ─╮\n  │\n[…]\no — same logic\n  - Orphaned helpers gone: KeyMap.ShortHelp, KeyMap.MiniShortHelp,\n    Model.renderShortHelp, package-level renderBindings\n  - bubbles/key import removed from view.go (no longer needed)",
          "is_bot": false,
          "headline": "TUI: drop the inline help bar, embed \"? help\" in the bottom border",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:33:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7e501b685fb5ab09199d570fb5067fd31f741bd2",
          "body": "Three follow-up tweaks on the volume widget in the frame's title\nbar:\n\n  - VolLabel switches from Muted to Primary so the speaker icon\n    matches the fill cells in color. Icon + bar now read as one\n    unified \"volume control\" rather than icon (decorative) + bar\n    (the actual indicator).\n  - The \n[…]\nighter in the title bar\n    without the components feeling cramped.\n\nVolPercent style stays — it's still used by the --statusline CLI\nmode where the bar isn't shown and the digit is the entire signal.",
          "is_bot": false,
          "headline": "TUI: tighten volume widget — purple icon, no percent, less padding",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:27:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6a077f86ee87bb5a144b33b343392b5a5349f0ce",
          "body": null,
          "is_bot": false,
          "headline": "TUI: drop Bold on volume icon — pointless on the MD glyph",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:24:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3fa77fbe158cffdac713b583d1a32ae62565e0f3",
          "body": "Tried the FA volume-up (small-but-with-waves) and Bold-FA variants\nsince the previous MD switch — neither gave the visual weight the\ntitle bar needed. Settle on MD volume_high (U+F057F, \"󰕿\"): heavier\nsilhouette, quiet muted gray, sits at proper size in the title\nbar. The lack of sound-wave arcs is a known trade-off the user\naccepts for the visual presence.\n\nBold on the volume label is dropped — pointless on the MD glyph\nwhich renders identically.",
          "is_bot": false,
          "headline": "TUI: switch volume icon back to MD volume_high silhouette",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:24:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b56ee24bf67f4522dea5326916d71546138c6487",
          "body": "The FA volume-up glyph reads as light/thin in the title bar. Apply\nBold to the icon style so terminals with a heavier weight in their\nNerd Font show a thicker speaker; on fonts without that variant the\nfallback is identical to before, no layout break.",
          "is_bot": false,
          "headline": "TUI: bold the volume icon for a heavier glyph render",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:22:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "21051d20c5914d5219acf7e6b2f7f2642aefe795",
          "body": "The Unicode 🔊 emoji ended up reading as a color 3D button that broke\nthe flat muted style and sat taller than a single text row. Drop the\nemoji and go back to FA volume-up (U+F028, \"\"). It's small, but\nin-style with the rest of the Nerd Font icons (logo, stations) and\nsits cleanly inline on the title bar.",
          "is_bot": false,
          "headline": "TUI: revert volume icon to plain FA volume-up",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:21:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9dcc711ede6bd40495fe1d00d8969da3adb4dd48",
          "body": "Both Nerd Font candidates were either too small (FA volume_up ),\nunrecognized on the user's font (FA broadcast-tower , rendered as\n\"≡↑\"), or sound-wave-less (MD volume_high 󰕿). Drop the Nerd Font\ndependency for this glyph and use 🔊 (U+1F50A SPEAKER WITH THREE\nSOUND WAVES), the standard Unicode emoji\n[…]\no font-dependent fallback. The\nnative emoji color may override the Muted style we apply, which is\nfine for this use: the speaker is meant to read as a recognizable\nicon, not as a quiet section marker.",
          "is_bot": false,
          "headline": "TUI: use the unicode speaker emoji for the volume icon",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:18:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f78375e86e549c8f29fba51b5354cd35540a19e1",
          "body": "FA volume_up () was visually too small for the title bar — it\nread as an afterthought next to the bar. FA broadcast-tower ()\nis heavier in most Nerd Fonts and clearly signals \"broadcasting\nsound\" with the antenna + wave arcs, so the volume widget reads\nwith proper weight at the top-right of the frame.",
          "is_bot": false,
          "headline": "TUI: bigger volume icon — switch to FA broadcast-tower",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:17:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0a189c8524e1ce1dd12f862e6bcd05299e060d3d",
          "body": "Two icon walk-backs after the user clarified what they actually\nmeant:\n\n  - Logo glyph reverts from headphones () to the original music\n    note (♪). The headphones change was based on a misread of the\n    user's request — they were asking about the volume icon, not\n    the logo.\n  - Volume icon rev\n[…]\nith no sound waves, which read as \"mute\" / no\n    audio. FA volume_up has the radiating wave arcs that make the\n    speaker visually \"active\" — exactly the \"playing audio\" feel\n    the user was after.",
          "is_bot": false,
          "headline": "TUI: revert logo to music note, switch volume back to FA volume-up",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:16:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f92f4d406085acba438e6ab3ceac178f4f7dab8f",
          "body": "The static ♪ note read as \"this is sheet music\" rather than \"this is\nplaying audio\". Headphones (FA U+F025, \"\") put the brand into a\nlistening posture — visually the title bar now signals \"you're\nhearing something through this app\" instead of \"music notation\".",
          "is_bot": false,
          "headline": "TUI: swap logo glyph from music note to headphones",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:14:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dfd8bb3879df135dfd4bfbf1faf02eb5738d643b",
          "body": "Two related polish points: the spring-animated volume bar made the\ntitle bar dance during +/- presses (filler hyphens shifted left and\nright as the percent string changed width 80% → 100%), and the user\npreferred discrete 5%-jumps over the smooth fill.\n\nDrop the spring animation entirely:\n  - intern\n[…]\n.100 range, so the\nfiller \"─\" segments don't jitter when the digit count changes.\n\nrenderVolume reads m.volume directly; the percent and the bar\nnaturally agree because they share the source of truth.",
          "is_bot": false,
          "headline": "TUI: instant volume changes with fixed-width percent",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:12:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f164acbaa93bd2a404e4925954f34f659af4882b",
          "body": "Two fixes for the volume widget in the title bar:\n\n1. The percent digit was reading m.volume (the target) while the bar\n   filled cells came from m.volumeDisplayed (the spring-animated\n   value). On +/- the digit jumped instantly to \"85%\" while the bar\n   was still climbing through 80→81→...→85, so \n[…]\nn a void\". Muted gives the empty cells\n   enough contrast to read as a quiet placeholder rail, so the\n   filled cells look like progress drawing across a track instead of\n   appearing out of thin air.",
          "is_bot": false,
          "headline": "TUI: sync volume percent with the animated bar; brighter empty rail",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:08:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a0246a2bd257e3f23069263b9282d5aa773cb6bc",
          "body": "Volume bar + icon + percent now live as the right-side label in the\nframe's top edge, replacing the clock. The body of the app drops the\ndedicated volume row entirely:\n\n  ╭─ ♪ lofi.player ─────────  󰕿  ▰▰▰▰▰▰▱▱▱▱▱▱  85% ─╮\n  │                                                  │\n  │  ● Lofi Girl 24/7 \n[…]\nsegments.\n\nrenderVolume keeps its previous spring-animation logic (volumeDisplayed\nticks toward volume, see anim.go) — the rendered output now happens to\nland in the title bar rather than its own row.",
          "is_bot": false,
          "headline": "TUI: move volume widget into the frame's top border",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T18:03:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "635f22e77821056c8c51c951d0a4c02291798b93",
          "body": "Symmetric labeling: stream-kind rows get \"· stream\" instead of being\nunmarked. Both source types are now visually equal — the user can\nalways tell which entries route through ytdl_hook and which hit mpv\ndirectly, without having to remember which one is the default.\n\nstationKindIcon switches from a YouTube-only conditional to printing\n\"· \" + EffectiveKind() for any non-empty kind (so a future custom\nkind shows up too instead of silently disappearing).",
          "is_bot": false,
          "headline": "TUI: tag every station with its kind (\"· youtube\" / \"· stream\")",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T17:58:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1f10f32ee578939e84cf2e4b528d131eff2db3e6",
          "body": "The \"· yt\" abbreviation was unfamiliar enough to read as noise.\nSpell out \"· youtube\" — extra characters are worth it for the\nzero-ambiguity reading. The rule \"tag the exception, not the\ndefault\" stays: stream (default) entries still get no marker, only\nYouTube-kind rows pick up the muted suffix.",
          "is_bot": false,
          "headline": "TUI: spell out \"youtube\" instead of the cryptic \"yt\"",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T17:56:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "956789252e7feffb8861559bb94d7b1777975885",
          "body": "The Nerd Font youtube codepoint (U+F167) doesn't render reliably —\non the user's terminal it showed up as a generic ◇ diamond, which\ndefeats the whole point of an at-a-glance source indicator.\n\nSwitch to a small textual tag in muted: \"· yt\" appended after the\nstation name on YouTube-kind rows. Strea\n[…]\n stays clean and the rare\nYouTube entry visually stands out without the eye having to parse a\nrss-vs-yt distinction on every line.\n\nRemoves the now-unused iconKindYouTube and iconKindStream constants.",
          "is_bot": false,
          "headline": "TUI: textual \"· yt\" tag for YouTube stations, drop unreliable glyph",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T17:54:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a6a1b45629a69df6036b631d7866e78f65d57af8",
          "body": null,
          "is_bot": false,
          "headline": "Remove stray view.go.orig left from a patch attempt",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T17:52:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97ab1995d52e9b94f8bc3965c3aa6d09f3525ecf",
          "body": "Each station picks up a tiny Nerd Font icon indicating its source:\n (FontAwesome youtube) for Kind=youtube, and  (FontAwesome rss)\nfor Kind=stream — direct HTTP/Icecast streams. The glyph is rendered\nin the SectionHeader (Muted) tone, so it sits as a quiet tag next\nto the bold station name without c\n[…]\nname, so a quick visual scan shows which entries route through\n    yt-dlp.\n\nstationKindIcon is the only render helper added; it falls back to\nempty string for unknown kinds rather than rendering tofu.",
          "is_bot": false,
          "headline": "TUI: station-kind glyph next to station names",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T17:51:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "494bac2b3866137ffdb4479c9af7195047c6e73f",
          "body": "While a station is playing (loading flag cleared), the live ●\nindicator now alternates between full intensity and dimmed every\n700 ms via the SGR Faint attribute. Calm 1.4 s breath, no color\nchange — reads as \"alive\" rather than \"blinking alarm\".\n\nImplementation:\n  - new pulseTickMsg + pulseTick com\n[…]\np (one bool flip + one tea.Tick reschedule)\n\nFaint chosen over per-color RGB math so the pulse adapts to the\ncurrent theme automatically — every theme's StatusLive picks up the\ndim treatment for free.",
          "is_bot": false,
          "headline": "TUI: soft heartbeat pulse on the live ● indicator",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T14:04:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "70e822c4f03bbe3fca668c3222fb27ff5ffc3153",
          "body": "The brand and the clock now ride the top edge of the outer frame,\ntitle-bar style:\n\n  ╭─ ♪ lofi.player ──────────────────  16:57 ─╮\n\nThis frees the row inside the frame that the standalone header was\noccupying, makes the app look more like a self-contained desktop\nwindow, and removes the visual repe\n[…]\nline top and bottom gives the content vertical breath.\n\nviewFull and viewMini drop their renderHeader call (the brand has\nmoved to the frame). renderHeader is removed since nothing else\nreferences it.",
          "is_bot": false,
          "headline": "TUI: title and clock embedded in the frame's top border",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T14:03:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d654df26cabd5d8b10ef4fe8d5f518d02ec1acfa",
          "body": "The frame was stretching across the full terminal width. On a 200+\ncell screen the content hugged the left edge with a sea of empty\nspace on the right. Pin the frame to 100 cells and center it with\nlipgloss.PlaceHorizontal — on wide terminals the app reads as a\ncontained panel sitting in the middle \n[…]\ntent starts wrapping awkwardly).\nInner content width passes through as frameWidth - 2 so headers\nand the now-playing track-line truncation budget align inside the\nborders the same way they did before.",
          "is_bot": false,
          "headline": "TUI: center the app frame at a fixed width on wide terminals",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:56:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5fb6f46f2b0af0f6b652626b7048d3e783e9a788",
          "body": "Two nested rounded borders (outer \"app\" + inner \"now playing\")\nwere competing for attention. Drop the inner one — the outer frame\nis now the only container on screen, and now-playing reads as the\ntop section inside that frame, given visual weight by the bold\nstation name and the leading ●/◯/spinner \n[…]\no leftPad-prefixed lines.\nThe track-line truncation budget tracks innerWidth (terminal width\nminus leftPad on both sides, capped at nowPlayingMaxWidth) so long\ntitles still get the ellipsis treatment.",
          "is_bot": false,
          "headline": "TUI: drop the inner now-playing card, keep the outer app frame",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:52:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5eff956b9e357e5905f60d3b8dea72c134dd4bc8",
          "body": "The whole UI now lives inside a single rounded card so the layout\nreads as one contained surface instead of a stack of unrelated rows\nfloating in the terminal. Border color is Subtle (one rung quieter\nthan the now-playing card's Muted) so the two borders coexist —\nouter reads as \"app frame\", inner r\n[…]\ntal padding stays at 0 so the existing leftPad inside content\nkeeps doing the indent (no double-indent).\n\nNo changes to behavior or any other rendering logic — the wrap is a\nsingle addition in View().",
          "is_bot": false,
          "headline": "TUI: wrap the entire app in an outer rounded border",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:48:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "37d80348263b5f68e27e34da96269071ea033c06",
          "body": "…ding\n\nThree small follow-ups from the latest screenshot:\n\n- Volume bar widens from 10 cells to 14 cells. The bar was reading\n  as a token next to the now-playing card; 14 cells gives 5%-step\n  changes a noticeable visual jump and brings the bar closer to the\n  card's visual weight.\n- Adds back a bl\n[…]\ncells of breathing room reads as\n  intentional layout rather than \"barely indented\". Card MarginLeft\n  follows automatically since it derives from len(leftPad).\n\nNo behavior changes; tests stay green.",
          "is_bot": false,
          "headline": "TUI cosmetic pass: wider volume bar, breathing room, premium left pad…",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:41:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6f3a4db6c5669a513391c7a0ddf1b5963fc18000",
          "body": "Five small visual tweaks the user pointed out from the latest screenshot:\n\nThe now-playing card no longer stretches across the full terminal\nwidth. nowPlayingMaxWidth = 80 caps it; on a 200-cell terminal the\ncard now reads as a focused element rather than a stretched panel\nwith one line of content f\n[…]\nons · 5\".\nTrivial information density boost — also balances the leading icon.\n\ngo vet + go test still green; no test changes needed since none of\nthese touched user-visible behavior beyond aesthetics.",
          "is_bot": false,
          "headline": "TUI cosmetic pass: card width, help labels, cursor color, station count",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:25:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a717e43f7a5d58255b55d576ad9a5807546d71e9",
          "body": "The loading flag in the TUI was getting stuck while music was already\nplaying because mpv's pause property doesn't fire a change event when\nloadfile transitions from one playing stream to another (false → false\nisn't a \"change\"). The TUI was waiting for PlaybackStarted that would\nnever arrive.\n\nSwit\n[…]\nner to\nflip back to ●.\n\nThe pause property handler stays as a fallback for genuine\npause/resume transitions where the property does change, so toggling\nspace on the same station still works as before.",
          "is_bot": false,
          "headline": "audio: emit PlaybackStarted on mpv playback-restart event",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:18:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "62f87746ead8ef3cf8abcb29860b6ebb5e9ab562",
          "body": "User decided this added complexity without enough day-to-day value.\nThe git history (commit 91c5d1f) preserves the full implementation;\nthis removes it from the runtime cleanly.\n\nDeleted packages:\n  - internal/pomodoro: state machine + Stats type + tests\n  - internal/notify: OS desktop notifications\n[…]\n streak +\nlistened mentions.\n\ngo.mod stays the same — no external deps were unique to pomodoro.\nAll tests still pass, no exported audio/config/state/theme/tui API\nbeyond pomodoro pieces was disturbed.",
          "is_bot": false,
          "headline": "Drop pomodoro / streak / listened-time feature",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:11:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "96e9064f2f4b4effccc572a6bdcb5af5a7ed1387",
          "body": "The spinner that lived in the track line (\"buffering metadata\") fired\nwhile audio was already playing — a visible animation during normal\nlistening was noise, not information. Revert that branch back to the\nquiet \"…\" placeholder.\n\nThe status-indicator spinner stays as is: it animates only while\nloading is true (between dispatching playCmd and the first\nPlaybackStarted event from mpv), which is the genuine \"not yet\naudible\" window.",
          "is_bot": false,
          "headline": "TUI: drop the track-line spinner, keep the status one",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:02:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f5ac3ee7cdadd022906364b6c2582c03203d772d",
          "body": "A new \"loading\" flag on Model tracks the gap between dispatching a\nplayCmd (or autoplay on startup) and the first PlaybackStarted event\nmpv sends back. While loading is true, the status indicator — both\nin the now-playing card and beside the playing-station row in the\nlist — renders the existing bra\n[…]\nrefreshes spinner.Style so the spinner color tracks the active theme.\nTestView_ShowsPlayingMarker updated to drive the model through the\nloading → playing transition explicitly via PlaybackStartedMsg.",
          "is_bot": false,
          "headline": "TUI: spinner in status indicator while mpv is buffering",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T13:00:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1ec6e89c7c345856134be910a1b0c5668d766c94",
          "body": "Static \"…\" gave no signal that the player was actually doing\nsomething while ICY/media-title metadata was in flight. Replace it\nwith bubbles/spinner.Dot — the canonical braille spinner\n\"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\", 10 FPS — followed by a muted \"buffering\" label.\n\nSpinner ticks live globally: Init returns m.spinner.\n[…]\nrendered and the tick is a no-op.\n\nSpinner color uses the active theme's Muted tone so the animation\ndoesn't dominate the now-playing card; it reads as a quiet activity\nindicator rather than an alarm.",
          "is_bot": false,
          "headline": "TUI: braille spinner for buffering placeholder",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:56:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fc64578c3f638b52884fc440c5d6f22f953b2939",
          "body": "mpv's ytdl_hook briefly reports a YouTube URL tail\n(\"watch?v=jfKfPfyJRdk\") as media-title before the resolved video\ntitle arrives a few seconds later. The TUI was rendering the URL\nfragment as the track line (\"watch?v=jfKfPfyJRdk\" in muted under\nthe station name), which looks broken.\n\ntranslatePrope\n[…]\n…\" placeholder\ninstead, which then gets replaced by the real title once\nytdl_hook resolves it.\n\nTable-driven test covers the YouTube case plus a few generic URL\nshapes (http, rtmp, query-string-only).",
          "is_bot": false,
          "headline": "audio: suppress URL-fragment media-title from ytdl_hook",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:53:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5a9789d3133d7faf400c9fbc3363ac82fbf23dc6",
          "body": "The \"+ add station\" affordance under the list duplicated the\n\"a add station\" entry already in the help bar — two parallel paths to\nthe same action, both visually present at the same time. Drop the\ninline line; the help-bar entry stays. The empty-list case keeps the\n\"press a to add one\" hint so a fre\n[…]\n\\n stacked with\nthe gap and inflated the spacing.\n\nNet: same elements, tighter layout, no information loss. Orphan\nAddStation style removed from styles.go since its only consumer\nwas the deleted line.",
          "is_bot": false,
          "headline": "TUI polish: drop \"+ add station\", tighten inter-block spacing",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:50:38Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4dc342553614ed0087fb08f131a67d6980cf0fb1",
          "body": "Three small follow-ups after the previous reskin landed:\n\nformatTrack now distinguishes \"real ICY metadata\" from \"media-title\nfallback\". When mpv has both Artist and Title (the normal Icecast\ncase), the line stays in foreground + warning accent — the user is\nlistening to a song. When only Title exis\n[…]\nank line between the last station and \"+ add station\" is\nremoved too — the affordance now reads as part of the list rather\nthan a stranded line below it.\n\nTests still pass; only renderer code shifted.",
          "is_bot": false,
          "headline": "TUI polish: track styling, spacing, card padding",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:46:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "55c65a344ecaaae5edecb4da8661e369cb6af4a3",
          "body": "Visual reskin pulling the UI toward modern-dashboard territory while\nkeeping it minimal: icons earn the identity, a single rounded card\nanchors the focus element, and the rest stays free-form.\n\nNow-playing wraps in a rounded-border lipgloss card (Muted border,\nhorizontal padding, MarginLeft to align\n[…]\n new\nindicator glyphs (TestView_ShowsPlayingMarker now asserts ● / ◯\ninstead of \"live\"/\"paused\"). Orphan Separator style removed from\nstyles.go — its only consumer was the now-deleted renderSeparator.",
          "is_bot": false,
          "headline": "TUI: nerd-font icons, now-playing card, status glyphs",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:42:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5b7eb3cfeb99f9d5f0663b042d35082bfebf4d2a",
          "body": "Two real rendering bugs surfaced on the user's first screenshot:\n\nA1. Column alignment in pomodoro / today panels was busted because\nfmt.Sprintf(\"%-Ns\", styled, ...) counts ANSI escape bytes toward the\nfield width, so styled labels got zero padding (\"listened<1m\" instead\nof \"listened   <1m\"). Replac\n[…]\ninstead of \"(no stations configured)\" — fresh-install users see\nthe next step without consulting docs.\n\ngo vet + go test stay clean. No exported API changes — only renders\ninside internal/tui shifted.",
          "is_bot": false,
          "headline": "TUI polish: column alignment, layout balance, cursor + empty states",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:28:07Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ad4859cab5c900224f50c4d6ad4573c326ca18dc",
          "body": "The plan §5.1 mockup hinted at this with a \"+ add station\" placeholder\nin the station list, but until now the only way to add a station was\nto edit config.yaml by hand. New `a` binding pops a centered modal\nform built on bubbles/textinput: name + url, Tab cycles fields, Enter\nsaves, Esc cancels. Val\n[…]\n.\n\ngo.mod / go.sum: add the bubbles/textinput transitive dep\n(github.com/atotto/clipboard).\n\nREADME updated with the new binding and a one-liner explaining the\nmodal flow plus the auto kind detection.",
          "is_bot": false,
          "headline": "TUI: add-station modal form (a key)",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:13:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c1f891cb36a05dd44c00e3e58fbf00ff9b4f0385",
          "body": "adrg/xdg.ConfigFile/StateFile resolves to ~/Library/Application Support\non macOS, despite the project plan §9 explicitly wanting the XDG-style\n~/.config and ~/.local/state paths on both platforms (the convention\nterminal-tool users expect). Result: the README pointed users at one\nlocation while conf\n[…]\n\nfile is absent or on Linux).\n\nREADME: rewords the config / state sections to match reality and to\nreassure users that no manual file or directory setup is needed —\nboth are auto-created on first run.",
          "is_bot": false,
          "headline": "Fix XDG path resolution on macOS, drop adrg/xdg dep",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T12:06:07Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5a059bfd63f08cca4323cced9bb9b3bd0c57b639",
          "body": "config.Station gains a Kind field with two recognized values: \"stream\"\n(default — direct HTTP/Icecast URL passed to mpv as-is) and \"youtube\"\n(routed through mpv's built-in ytdl_hook script). Empty Kind defaults\nto \"stream\" so existing Phase 0-3 configs keep working with no changes.\nHelpers Effective\n[…]\nkey parsing back to KindStream, and YAML round-trip of\nkind: youtube. Example config gets a commented-out Lofi Girl entry as\na starting template; README documents the new requirement and config\nshape.",
          "is_bot": false,
          "headline": "Phase 4a: YouTube station kind via mpv's ytdl_hook",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T11:56:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "91c5d1f7bcb444d7584e0639e86b29a60cbca3ae",
          "body": "internal/pomodoro: pure state machine — no goroutines, no time.Now()\ninside the package. Sessions store an absolute EndsAt rather than a\ncountdown so laptop sleep / suspend doesn't drift the timer; Tick\nadvances exactly one phase per call even when \"now\" is far past\nEndsAt. Stats track ListenedToday\n[…]\nts, partial overrides keep defaults for unmentioned\nfields, and `pomodoro:` (null) re-fills defaults.\n\nREADME updated to Phase 3 surface; the example config now includes the\ndocumented pomodoro block.",
          "is_bot": false,
          "headline": "Phase 3: pomodoro focus timer with stats and notifications",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T10:03:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9a0190cbc1855bfe9407a6571c4a7d9ac8450396",
          "body": "Themes. Three new palettes (catppuccin-mocha, gruvbox-dark, rose-pine)\njoin Tokyo Night, all sharing the same 10-role surface. theme.Names()\nexposes a stable ordering and theme.Next drives the new `t` cycle\nbinding; switching is instantaneous because Styles is just rebuilt\nfrom the new palette.\n\nSta\n[…]\norizontally centered on the available width.\n\nREADME updated to Phase 2 surface; tests cover theme registration,\nstate round-trip + atomicity + Pomodoro preservation, and the existing\nTUI smoke suite.",
          "is_bot": false,
          "headline": "Phase 2: themes, state persistence, mini mode, statusline, polish",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T09:26:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5e246379d4055c7b29a2a19fc9cc37caae1e5f9b",
          "body": "internal/audio: new package wrapping the mpv subprocess. ipcClient\ndemultiplexes JSON-line messages by request_id (sync.Map), serializes\nwrites through a mutex, and runs a 1 MiB-cap scanner read loop that\nroutes property-change events to a buffered channel. Player layers the\nhigh-level surface on to\n[…]\nerror responses, large lines, malformed input,\ncontext cancellation, post-close behavior); Player.translateOne for\nall event variants and dedup behavior. Manual mpv integration is the\nacceptance gate.",
          "is_bot": false,
          "headline": "Phase 1: real audio via mpv JSON-IPC",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T09:15:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7b20105079553e92ce6dd63aa2daab1336f1d445",
          "body": "Bootstraps the Bubble Tea TUI per plan §6 Phase 0. Audio is faked —\nspace toggles a \"live\"/\"paused\" status flag on the cursor's station and\nmarks it with a ♪ glyph. Real playback arrives in Phase 1 via mpv IPC.\n\ninternal/config: YAML loader on the XDG path. Defaults() ships four\nSomaFM/Radio Paradis\n[…]\nt from §5.1.\n\nTests cover the core (config round-trip, defaults, malformed YAML;\ntheme Lookup; volume clamping, cursor bounds, play/pause toggle, help\ntoggle, quit). View rendering is verified by eye.",
          "is_bot": false,
          "headline": "Phase 0: UI scaffold with Tokyo Night theme",
          "author_name": "Alexander Neupokoev",
          "author_login": "Alexsatter",
          "committed_at": "2026-04-25T08:41:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 13,
      "commits_last_year": 101,
      "latest_release_at": "2026-06-18T09:43:49Z",
      "latest_release_tag": "v0.1.12",
      "releases_from_tags": false,
      "days_since_last_push": 36,
      "active_weeks_last_year": 5,
      "days_since_latest_release": 36,
      "mean_days_between_releases": 5.1
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 42,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/iRootPro/lofi-player",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/iRootPro/lofi-player",
          "is_deprecated": false,
          "latest_version": "v0.1.12",
          "repository_url": "https://github.com/iRootPro/lofi-player",
          "versions_count": 13,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-06-18T09:42:44Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 36
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 1,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 34574,
      "source_files_sampled": 37,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "go.mod"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "go"
      ],
      "dependencies": [
        {
          "name": "github.com/atotto/clipboard",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.1.4"
        },
        {
          "name": "github.com/charmbracelet/bubbles",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.0.0"
        },
        {
          "name": "github.com/charmbracelet/bubbletea",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.3.10"
        },
        {
          "name": "github.com/charmbracelet/lipgloss",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.1.0"
        },
        {
          "name": "gopkg.in/yaml.v3",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v3.0.1"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "Alexsatter",
          "commits": 101,
          "avatar_url": "https://avatars.githubusercontent.com/u/118963855?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "go.sum"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 0,
            "reason": "project was created within the last 90 days. Please review its contents carefully",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 10,
            "reason": "GitHub workflow tokens follow principle of least privilege",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 8,
            "reason": "2 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "9a340a77e7668a24327a26d62771287328ed94ac",
        "ran_at": "2026-07-24T21:36:50Z",
        "aggregate_score": 3.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-06-18T09:43:40Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/iRootPro/lofi-player",
    "host": "github.com",
    "name": "lofi-player",
    "owner": "iRootPro"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "at_risk",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 42,
      "inputs": {
        "security": 34,
        "vitality": 60,
        "community": 24,
        "governance": 34,
        "engineering": 56
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "moderate",
        "name": "Vitality",
        "value": 60,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "at_risk",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "commits_last_year": 101,
              "human_commit_share": 1,
              "days_since_last_push": 36,
              "active_weeks_last_year": 5
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 36 days ago",
                "points": 18,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 36
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "5/52 weeks with commits",
                "points": 3.5,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "101 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 101
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "releases_count": 13,
              "latest_release_tag": "v0.1.12",
              "releases_from_tags": false,
              "days_since_latest_release": 36,
              "mean_days_between_releases": 5.1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "13 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 13
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 36 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 36
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~5.1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 5.1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "critical",
        "name": "Community & Adoption",
        "value": 24,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 1,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "1 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 34,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "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": 44,
            "inputs": {
              "followers": 0,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "iRootPro",
              "public_repos": 81,
              "account_age_days": 3734
            },
            "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": "0 followers of iRootPro",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 0,
                      "login": "iRootPro"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "81 public repos, account ~10 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 81
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 10
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "github.com/iRootPro/lofi-player"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 36
            },
            "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 36 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 36
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "13 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 13
                    }
                  }
                ],
                "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": 56,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 60,
            "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": "1 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": false,
              "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": 0,
                "status": "missed",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 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": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 34,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Packaging. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "packaging"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 34,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.4
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "GitHub workflow tokens follow principle of least privilege",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "2 existing vulnerabilities detected",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 58,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.97,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "97 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 97,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 56,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [
                "go.mod"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "go.mod (toolchain convention, no task runner)",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "toolchain_convention",
                    "params": {
                      "files": "go.mod"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Go (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "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 0",
                "points": 0,
                "status": "missed",
                "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": 34574,
              "source_files_sampled": 37,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Go (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/37 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 37,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-24T21:36:54.976794Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/i/iRootPro/lofi-player.svg",
  "full_name": "iRootPro/lofi-player",
  "license_state": "standard",
  "license_spdx": "MIT"
}

Las puntuaciones son señales, no garantías. Reflejan prácticas públicamente visibles en GitHub; no son una auditoría de código ni una garantía de seguridad.

Los datos ausentes se excluyen y los pesos se renormalizan; nunca se puntúan como cero. La metodología es versionada y abierta: métricas v1.13.0, esquema v0.27.0 — metodología completa · wiki de métricas.

Cómo se sitúa un resultado dentro del registro general: estadísticas agregadasGo.