Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-28 11:53 UTC

tedilabs / ota

♥️ k9s for Okta — a Go/Bubbletea TUI to inspect Okta admin resources

GoApache-2.0★ 2 estrellas⑂ 0 forksdesde jun 2026Ver en GitHub ↗

tedilabs/ota tiene un índice de salud de 57 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Engineering Quality (68/100) y la más baja, Community & Adoption (34/100). Se actualizó por última vez hace 8 días. Una sola persona concentra la mayor parte del trabajo reciente.

57
global / 100
Moderado

Í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.

57
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

TedilabsOrganización
113 seguidores42 repositorios públicosdesde dic 2020

Este repositorio está respaldado por una organización: una custodia compartida y responsable que puede sobrevivir a cualquier mantenedor individual.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicación
Gogithub.com/tedilabs/otav0.1.2-2hace 8 días

Métricas por categoría

Vitalidad

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

67Moderado · 22% del índice global
Cómo se puntúa
28.8/36Recencia de push — último push hace 8 días
4.2/36Cadencia de commits — 6/52 semanas con commits
18/18Volumen de commits — 156 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_year156
human_commit_share1
days_since_last_push8
active_weeks_last_year6
Cómo se puntúa
27/27Publica versiones — 2 versiones publicadas
36/36Recencia de las versiones — última versión hace 8 días
27/27Cadencia de publicación — una versión cada ~0 días
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Datos de entrada utilizados
releases_count2
latest_release_tagv0.1.2
releases_from_tagsno
days_since_latest_release8
mean_days_between_releases0

Comunidad y Adopción

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

34En riesgo · 18% del índice global
Cómo se puntúa
0/60Estrellas — 2 estrellas
0/25Forks — 0 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks0
stars2
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 (Apache-2.0)
0/18Guía CONTRIBUTING
13.5/13.5Código de conducta
0/7.2Plantilla de issues
6.3/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributingno
has_issue_templateno
has_code_of_conduct
has_pull_request_template

Sostenibilidad y Gobernanza

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

59Moderado · 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
10/10OpenSSF Scorecard: Contributors — project has 9 contributing companies or organizations
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
38.2/38.3Aceptación de PR — 2/2 PR decididos fusionados
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs2
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues. Los pesos restantes se han renormalizado.
Cómo se puntúa
30/30Respaldo de la propiedad — propiedad de una organización
0/20Dominio verificado
14.8/25Alcance del propietario — 113 seguidores de tedilabs
23.2/25Trayectoria — 42 repos públicos, cuenta de ~5 años
Datos de entrada utilizados
followers113
owner_typeOrganization
is_verified
owner_logintedilabs
public_repos42
account_age_days2061
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en go
35/35Recencia de publicación — última publicación hace 8 días
12/20Historial de versiones — 2 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesgithub.com/tedilabs/ota
ecosystemsgo
any_deprecatedno
min_days_since_publish8

Calidad de Ingeniería

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

68Moderado · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 1 flujo(s) de trabajo
24/24Pruebas presentes
16/16Configuración de linter — .golangci.yml
0/9.6Hooks de pre-commit
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — 0 out of 2 merged PRs checked by a CI test -- score normalized to 0
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_config
has_precommit_configno
Cómo se puntúa
30/30README
25/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
10/10Topics — 6 topics
0/10Wiki
Datos de entrada utilizados
topicshacktoberfest, lang-go, okta, sso, tedilabs, tui
has_wikino
homepage
has_readme
has_docs_dir
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?

50Moderado · 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 — 0 out of 2 merged PRs checked by a CI test -- score normalized to 0
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
2.5/2.5Contributors — project has 9 contributing companies or organizations
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
5/5Packaging — packaging workflow detected
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6/7.5Vulnerabilities — 2 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated18
scorecard_versionv5.5.0
checks_inconclusive0
scorecard_aggregate3,7
Cómo se puntúa
35/35Dependencias directas libres de avisos conocidos — ninguna dependencia directa tiene un aviso conocido
0/25Dependencias indirectas libres de avisos conocidos — el conjunto transitivo no es separable de las dependencias de desarrollo y prueba en este alcance
0/40Sin avisos pendientes — ningún aviso tiene fecha de publicación
Datos de entrada utilizados
sourceosv
advisories2
affected_packages2
assessed_packages41
unassessed_packages0
affected_by_severityunknown 2
direct_affected_packages0
Excluidos de la puntuación (sin datos o no aplicable): Dependencias indirectas libres de avisos conocidos, Sin avisos pendientes. Los pesos restantes se han renormalizado. Se cotejaron 41 dependencias resueltas con OSV. Este repositorio no publica ningún paquete que el índice resuelva, por lo que se evaluó en su lugar el grafo de dependencias del repositorio. Ese grafo mezcla fijaciones de desarrollo y prueba con las dependencias distribuidas, de modo que solo se puntúan las dependencias declaradas en tiempo de ejecución; los hallazgos transitivos se informan como contexto y quedan excluidos de la puntuación. No se analiza la alcanzabilidad.

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.

66Moderado · 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 — 99 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,99
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
18/18Arranque con un solo comando — Makefile
22/22Pruebas automatizadas
11/11Configuración de lint / formato — .golangci.yml
11/11Verificación estática de tipos — Go (tipado estático)
10/10Entorno reproducible — Dockerfile, 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_dockerfile
typed_language
bootstrap_filesMakefile
has_devcontainerno
has_linter_config
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)
54.6/55Tamaños de archivo manejables — 2/278 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languageGo
largest_source_bytes119.335
source_files_sampled278
oversized_source_files2

Datos clave

2estrellas de GitHub
1contribuidores
156commits en los últimos 12 meses
8días desde el último push
2versiones 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

Más detalle

OpenSSF Scorecard 3.7 / 10
3.7agregado

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-28 11:53 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
0CI-Tests0 out of 2 merged PRs checked by a CI test -- score normalized to 0
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
10Contributorsproject has 9 contributing companies or organizations
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
10Packagingpackaging workflow detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
10Security-Policysecurity policy file detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
8Vulnerabilities2 existing vulnerabilities detected
Dependencias directas 14
RegistroPaqueteRestricción de versiónManifiesto
Gogithub.com/atotto/clipboardv0.1.4go.mod
Gogithub.com/charmbracelet/bubbleteav1.3.10go.mod
Gogithub.com/charmbracelet/lipglossv1.1.0go.mod
Gogithub.com/charmbracelet/x/ansiv0.11.6go.mod
Gogithub.com/charmbracelet/x/exp/teatestv0.0.0-20260422141420-a6cbdff8a7e2go.mod
Gogithub.com/google/uuidv1.6.0go.mod
Gogithub.com/knadh/koanf/parsers/yamlv1.1.0go.mod
Gogithub.com/knadh/koanf/providers/filev1.2.1go.mod
Gogithub.com/knadh/koanf/v2v2.3.4go.mod
Gogithub.com/mattn/go-runewidthv0.0.19go.mod
Gogithub.com/muesli/termenvv0.16.0go.mod
Gogithub.com/stretchr/testifyv1.11.1go.mod
Gogopkg.in/natefinch/lumberjack.v2v2.2.1go.mod
Gogopkg.in/yaml.v3v3.0.1go.mod
Todas las dependencias 41

Conjunto completo de dependencias resueltas según el grafo de dependencias de GitHub: 14 paquetes directos y 27 indirectos (transitivos). El cierre transitivo es completo cuando el repositorio incluye un lockfile.

RegistroPaqueteVersiónRelación
Gogithub.com/atotto/clipboardv0.1.4directa
Gogithub.com/charmbracelet/bubbleteav1.3.10directa
Gogithub.com/charmbracelet/lipglossv1.1.0directa
Gogithub.com/charmbracelet/x/ansiv0.11.6directa
Gogithub.com/charmbracelet/x/exp/teatestv0.0.0-20260422141420-a6cbdff8a7e2directa
Gogithub.com/google/uuidv1.6.0directa
Gogithub.com/knadh/koanf/parsers/yamlv1.1.0directa
Gogithub.com/knadh/koanf/providers/filev1.2.1directa
Gogithub.com/knadh/koanf/v2v2.3.4directa
Gogithub.com/mattn/go-runewidthv0.0.19directa
Gogithub.com/muesli/termenvv0.16.0directa
Gogithub.com/stretchr/testifyv1.11.1directa
Gogopkg.in/natefinch/lumberjack.v2v2.2.1directa
Gogopkg.in/yaml.v3v3.0.1directa
Gogithub.com/aymanbagabas/go-osc52/v2v2.0.1indirecta
Gogithub.com/aymanbagabas/go-udiffv0.3.1indirecta
Gogithub.com/charmbracelet/colorprofilev0.4.1indirecta
Gogithub.com/charmbracelet/x/cellbufv0.0.15indirecta
Gogithub.com/charmbracelet/x/exp/goldenv0.0.0-20241011142426-46044092ad91indirecta
Gogithub.com/charmbracelet/x/termv0.2.2indirecta
Gogithub.com/clipperhouse/displaywidthv0.9.0indirecta
Gogithub.com/clipperhouse/stringishv0.1.1indirecta
Gogithub.com/clipperhouse/uax29/v2v2.5.0indirecta
Gogithub.com/davecgh/go-spewv1.1.1indirecta
Gogithub.com/erikgeiser/coninputv0.0.0-20211004153227-1c3628e74d0findirecta
Gogithub.com/fsnotify/fsnotifyv1.9.0indirecta
Gogithub.com/go-viper/mapstructure/v2v2.4.0indirecta
Gogithub.com/knadh/koanf/mapsv0.1.2indirecta
Gogithub.com/lucasb-eyer/go-colorfulv1.3.0indirecta
Gogithub.com/mattn/go-isattyv0.0.20indirecta
Gogithub.com/mattn/go-localereaderv0.0.1indirecta
Gogithub.com/mitchellh/copystructurev1.2.0indirecta
Gogithub.com/mitchellh/reflectwalkv1.0.2indirecta
Gogithub.com/muesli/ansiv0.0.0-20230316100256-276c6243b2f6indirecta
Gogithub.com/muesli/cancelreaderv0.2.2indirecta
Gogithub.com/pmezard/go-difflibv1.0.0indirecta
Gogithub.com/rivo/unisegv0.4.7indirecta
Gogithub.com/xo/terminfov0.0.0-20220910002029-abceb7e1c41eindirecta
Gogo.yaml.in/yaml/v3v3.0.3indirecta
Gogolang.org/x/sysv0.38.0indirecta
Gogolang.org/x/textv0.28.0indirecta
Avisos de dependencias 2

Este repositorio no publica ningún paquete que el índice resuelva, así que se evaluó su propio grafo de dependencias — 41 paquetes, que incluyen también fijaciones de desarrollo y prueba que nunca se distribuyen: 2 tienen avisos conocidos, de los cuales 0 son directas.

PaqueteVersiónRelaciónGravedadAvisosCorregido en
golang.org/x/sysv0.38.0indirectadesconocida10.44.0
golang.org/x/textv0.28.0indirectadesconocida10.39.0

Un aviso significa que la versión registrada en el grafo de dependencias cae dentro del rango afectado de un aviso. No se analiza la alcanzabilidad, y el grafo incluye fijaciones de desarrollo y prueba: un hallazgo puede referirse al utillaje y no al software distribuido.

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [
        "hacktoberfest",
        "lang-go",
        "okta",
        "sso",
        "tedilabs",
        "tui"
      ],
      "is_fork": false,
      "size_kb": 1888,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Go": 1584205,
        "Shell": 4591,
        "Makefile": 1767,
        "Dockerfile": 1028
      },
      "pushed_at": "2026-07-20T10:09:51Z",
      "created_at": "2026-06-15T06:29:14Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-22T17:41:16Z",
      "description": "♥️  k9s for Okta — a Go/Bubbletea TUI to inspect Okta admin resources",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "Apache-2.0",
      "default_branch": "main",
      "license_spdx_raw": "Apache-2.0",
      "primary_language": "Go",
      "significant_languages": [
        "Go"
      ]
    },
    "owner": {
      "blog": "https://tedilabs.com",
      "name": "Tedilabs",
      "type": "Organization",
      "login": "tedilabs",
      "company": null,
      "location": "Estonia",
      "followers": 113,
      "avatar_url": "https://avatars.githubusercontent.com/u/75496022?v=4",
      "created_at": "2020-12-04T17:21:01Z",
      "is_verified": null,
      "public_repos": 42,
      "account_age_days": 2061
    },
    "license": {
      "state": "standard",
      "spdx_id": "Apache-2.0",
      "raw_spdx": "Apache-2.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.1.2",
          "kind": "patch",
          "published_at": "2026-07-20T07:08:26Z"
        },
        {
          "tag": "v0.1.1",
          "kind": "patch",
          "published_at": "2026-07-20T06:35:51Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "ddbf4aa5d77be8e462333b88b56d5e798cb4e9ae",
          "body": null,
          "is_bot": false,
          "headline": "docs: use mise github backend instead of deprecated ubi",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-20T10:09:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "74696a06ab393f971abbb3febb4b1bcd80a8cd62",
          "body": "Also fixes the stale hardcoded version default (v0.1.14 -> dev) so\nnon-ldflags builds like 'go install' stop reporting a bogus release.",
          "is_bot": false,
          "headline": "docs: restructure README with Homebrew/mise/Docker install paths",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-20T09:03:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2127dac40e38714cae6a6b294a95781bf0af5835",
          "body": null,
          "is_bot": false,
          "headline": "ci(release): ship Homebrew formula instead of cask",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-20T07:05:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7342e57eead9a2cfb8fd575348710c23954cd1d9",
          "body": "…uild ids",
          "is_bot": false,
          "headline": "ci(release): map HOMEBREW_TAP_TOKEN secret and fix universal binary b…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-20T06:33:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f364f29b75d9d3041d301b406ae2bd3e9ba777e8",
          "body": null,
          "is_bot": false,
          "headline": "chore: add Apache 2.0 LICENSE",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-20T06:33:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bcdd915e0dfa71f74bce956914acb7f1e24ddee9",
          "body": null,
          "is_bot": false,
          "headline": "feat: z key toggles timestamp rendering between Local and UTC",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-07-05T14:49:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0504dd65aa8264d619a88b016b0abbe98d99b0db",
          "body": "…ee (#8)\n\n* feat(xray): :xray user shows Groups→Rules→Policies→Apps dependency tree\n\nMW-1 — a single screen that surfaces everything reaching the selected\nuser: their Groups, the Group Rules targeting those groups, the Apps\nassigned via each group, plus direct app assignments. Policies matching\nis o\n[…]\nanch before.\n\nAdds Test_XRay_LastUpdated_StableAfterLoad which pins a FakeClock,\ndrives the model to quiescence, advances the clock, and asserts\nLastUpdated() returns a stable, non-drifting timestamp.",
          "is_bot": false,
          "headline": "feat(xray): :xray user shows Groups→Rules→Policies→Apps dependency tr…",
          "author_name": "Byungjin Park (Claud)",
          "author_login": "posquit0",
          "committed_at": "2026-07-05T13:49:38Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "eaa13473c4ec45f81919683db75145716b01e5ad",
          "body": "* feat(palette): fuzzy subsequence matching in the : autocomplete\n\nPrefix matching only surfaced :us -> :users but :apitk yielded nothing.\nAdd subsequence fuzzy scoring so :apitk -> :api-tokens, :grules -> :grouprules.\nPrefix matches always outrank fuzzy hits; ties break on shorter candidate.\n\n- new\n[…]\n bounds.\n- overlay.CmdPaletteModel.View was re-stripping paletteHints on every\n  keystroke render. Precompute paletteHintsStripped at package init.\n- fuzzy_test.go pins the Unicode length-change case.",
          "is_bot": false,
          "headline": "feat(palette): fuzzy subsequence matching in the : autocomplete (#1)",
          "author_name": "Byungjin Park (Claud)",
          "author_login": "posquit0",
          "committed_at": "2026-07-05T12:16:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9fda7177b37f5c15a7ef3dc125b4bb0552a31208",
          "body": "Extend the Users s status-picker to every resource with an\nActive/Inactive lifecycle. Press s on the selected row (or anywhere\nin detail) and a centered modal lists the valid target statuses for\nthe resource; pick one with j/k + Enter and the existing\ndestructive-action confirm gate handles the are-\n[…]\nuser picker tests\nflow through unchanged (NewStatusPickerModel renamed to\nNewUserStatusPickerModel).\n\nHelp overlay gains the s entries for each list. 25 packages\nrace-clean across the full test suite.",
          "is_bot": false,
          "headline": "feat: s status-picker for Group Rules, Policies, Apps, Authenticators",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-20T17:49:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "530d656f9c9653e3514499ac46334710f5a57cce",
          "body": "Extend the Users edit-form pattern (REQ-W01 SCR-012) to every\nresource type whose Okta API surface accepts a profile / metadata\nupdate. Press e on the selected row in any list, or anywhere in\ndetail, to open the resource's edit popup; Ctrl+S saves; Esc shows\nthe discard picker.\n\nPer-resource scope:\n\n[…]\n its cached row on\nXxxUpdatedMsg so the post-save view reflects the change without an\nextra GET.\n\nHelp overlay gains the e key entries for each list. 25 packages\nrace-clean across the full test suite.",
          "is_bot": false,
          "headline": "feat: edit forms for Groups, Group Rules, and Policies",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T13:52:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "346f7119c61d1ef9e5e93503dac5382eebb4de03",
          "body": "…nterface\n\nPrepare for the multi-resource edit form rollout (Groups / Rules /\nPolicies) by moving the per-screen rendering helpers into the form\npackage so every resource shares one set of visuals:\n\n- BuildDiscardStrip / pickerOption / RenderPlaceholderBody /\n  ComposeFooter / RenderFormBody — all l\n[…]\nve screen\nthat implements RenderModal through the v2 popup-over-dimmed-body\npath. Drops the explicit ScreenUserEdit branch from composeBody;\nnew edit screens opt in just by implementing the interface.",
          "is_bot": false,
          "headline": "refactor(form): lift shared edit-screen helpers + add ModalRenderer i…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T13:50:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6cc97b955bce635b554b863abcfdbabc79aca1ad",
          "body": "…wiring)\n\nThe Esc-on-dirty prompt offered \"Discard and exit\" but pressing\nEnter on the highlighted option did nothing visible — the modal\nstayed open with the prompt still showing. Root cause: EditModel\nemitted form.DiscardRequestedMsg{Confirmed: true} and nothing\nhandled it, so it was effectively a\n[…]\n modal.\n\nQA-W01's existing \"dirty Esc opens Discard\" test only verified the\nprompt rendered, not that confirming actually exited. The new\nTest_AppShell_DiscardAndExit_PopsNav regression pins the exit.",
          "is_bot": false,
          "headline": "fix(users): discard-and-exit actually pops nav (UserEditDiscardedMsg …",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T12:57:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1e303b28800d56d280f406785e7f1e8c5b427d30",
          "body": "Three operator-reported issues on the v2 edit modal:\n\n1. CJK / unicode input was broken end-to-end. Cursor was tracked in\n   bytes but Backspace / Delete / Left / Right stepped one byte each,\n   leaving the cursor inside a multi-byte rune and corrupting any\n   subsequent edit. Walk by rune via utf8.\n[…]\no 1 (Keep editing) so a\n   stray Enter never destroys work.\n\nTwo new regression tests cover the rune-aware Backspace and the\nrune-aware arrow-key cursor walk for hangul input.\n\n25 packages race-clean.",
          "is_bot": false,
          "headline": "fix(users): unicode caret, read-only trail overflow, discard picker UX",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T12:57:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e862750235ba0f4a93949c92bf85cf2e3d33046d",
          "body": "…cycle transitions",
          "is_bot": false,
          "headline": "feat(users): status picker — s opens a select-box modal of valid life…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T12:56:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "421a65782c56e69582865ae1dae843c2f9c03e9b",
          "body": "The v2 edit modal lifted the focused row with a vertical-bar prefix\nand Accent box, but the caret position inside the value was\ninvisible — keystrokes appeared to land somewhere, with nothing\nshowing where. Operator feedback: \"cursor가 어딨는지 알 수가 없어.\"\n\nAdd Form.CursorPos() that returns the focused fie\n[…]\nonto the\npadded value. Reverse video is an SGR attribute rather than a color,\nso the caret stays visible under NO_COLOR / monochrome themes.\n\nThe plain Form.View() path (teatest goldens) is unchanged.",
          "is_bot": false,
          "headline": "fix(form): stamp a visible caret on the focused field",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T07:19:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0829133bb88df316945ddf090df3e2d0155c8187",
          "body": "… count\n\nVisual implementation of the SCR-012 v2 redesign. EditModel.View()\nstays plain (teatest goldens depend on it); a new\nEditModel.RenderModal(tk, width, bodyBudget) drives the production\npopup path.\n\nApp Shell wiring:\n- composeBody detects active == ScreenUserEdit, calls RenderModal,\n  then co\n[…]\ng a nested box — same key semantics (y / Enter discard, n /\nEsc keep editing), and the QA regression test's \"Discard\" string\ncheck still hits.\n\n25 packages PASS, race-clean; QA regression suite green.",
          "is_bot": false,
          "headline": "feat(users): SCR-012 v2 — centered modal + focus lift + dirty section…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T07:11:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b51fc8cc6c9e797209635bd4d9d5b4051371769a",
          "body": "Operator feedback on the v0.2.0 edit form was direct: the design\nfelt dated. v2 replaces the full-screen take-over with the same\nmodal pattern the Quit / Action confirm / Help / API recorder\noverlays already use, so the four confirmation/mutation surfaces\nshare one visual language.\n\nDecisions D-W17~\n[…]\nbracket fallback)\n- D-W24 DiscardConfirm renders nested over the outer modal body\n- D-W25 loading is a placeholder form + footer spinner\n\nPRD AC-1~AC-10 unchanged — only visual representation changes.",
          "is_bot": false,
          "headline": "docs(design): SCR-012 v2 — popup-over-dimmed-body redesign",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-18T07:11:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7495f9eec668362da487abb9f63ce1825b195fd7",
          "body": "QA cycle 1 surfaced 1 Critical (silent data loss on dirty Esc) + 5\nHigh issues at the App Shell boundary that the Phase 5 teatest\nscaffold missed (it wraps EditModel directly, bypassing the App\nShell's Esc precedence). Adds 4 FAIL-FIRST regression tests at the\nApp Shell layer to pin the fixes — Esc \n[…]\nt\ntrail — PRD draft + Okta domain input + design draft + design\nreviews + tech-design outline + req coverage matrix + test fail logs\n+ implementation report + green progression log + Phase 7 findings.",
          "is_bot": false,
          "headline": "test(users): REQ-W01 Phase 7 QA regression + report",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T13:11:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a5f1fce8ad68b3ff0ebcaa2892b8d4876ce1fef9",
          "body": "First profile-mutation surface in ota. Press `e` on a selected row in\nthe Users list or anywhere in User Detail to open the edit form;\nmodify the 11 standard-profile fields; Ctrl+S to save (sparse\npartial-merge POST), Esc to cancel (discard confirm when dirty,\ninert during a save).\n\n- internal/domai\n[…]\nto lifecycle/test types so the\nUsersPort interface change doesn't break test fakes.\n\nREQ-W01 AC-1~10 met, D-W1~D-W16 decided, all RED tests from Phase\n5 (32) flip GREEN. Race-clean across 25 packages.",
          "is_bot": false,
          "headline": "feat(users): REQ-W01 — profile edit form (e from list/detail)",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T13:11:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c8eca4e572f0152c5b5ba1cfeb17ae10637ffbf0",
          "body": "ARCHITECTURE §6.1 adds the `*string`-pointer UserProfilePatch +\nErrEmptyPatch sentinel; §6.2 extends UsersPort with UpdateProfile +\n6-class error contract; §6.8 documents `internal/tui/shared/form/`\n(FieldSpec / Form / ErrorMapper) as the reusable form widget package\nwith depguard-enforced isolation\n[…]\n-W01 AC matrix, the 11-field × dirty matrix harness, error-cause\nmapping table tests, partial-merge body assertion tables, and the\nadapter integration suite (PUT-forbidden guard, ErrEmptyPatch\nguard).",
          "is_bot": false,
          "headline": "docs(arch,conv,tests): REQ-W01 form widget + write surface",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T13:10:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "28d887db16d2c00ddf8a416f3bdca587de2fac92",
          "body": "PRD §5.6 adds the REQ-W* (Write/Workflow) namespace + REQ-W01 (Users\nProfile Edit Form). 11 editable standard-profile fields, login locked\nread-only, last-write-wins (Okta /users has no ETag), AC-1~10 with\nexplicit error matrix per HTTP status, D-W1~D-W16 decision matrix\ncovering mount mode, save se\n[…]\nndering. Cross-section\npatches: §3.4 palette (:edit / :e), §3.6 detail keys (e row),\n§10.1 risk levels (L0 save / L1 discard), §11.2a AC mapping\nmatrix, §12.1 key conflict table, §13 decisions #9-#15.",
          "is_bot": false,
          "headline": "docs(prd,design): REQ-W01 — Users profile edit form",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T13:10:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e2fbd51d6eb7429c8f4fffbc04228bd7583a4505",
          "body": "The Option A pivot (Phases A1–A4) made the home dashboard\nrate-limit-safe by deriving every headline metric from a single\n/api/v1/logs walk, but the result still didn't feel useful enough to\njustify the surface area: a whole screen, a cache package, palette +\nhelp entries, a HealthSnapshot bridge in\n[…]\nons, and the buildScreen branch\n- flip InitialScreen back to ScreenUsers (the iota=0 default)\n- drop defaultDashboardDir helper from wire.go\n- regenerate the palette golden so :users is the first hint",
          "is_bot": false,
          "headline": "revert(home): drop the home dashboard, restore Users as boot default",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T02:35:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cc9e76cd20ba1d04dc28b7edeff6b1ead2505973",
          "body": "…Δ footer\n\nAdds activity_internal_test.go covering countActivity / countPosture\nevent-type bucketing directly — every new EventType case (app\nmembership churn, role changes, policy mutations, lifecycle\nsuspends, distinct admin-actor dedup) is now pinned by an assertion\nso future bucketing tweaks fai\n[…]\nw. Pins the Activity→cache.Put wiring + DeltaFor read\npath end-to-end.\n\nSampled-flag flip test guards the single-page bound — busy tenants\nmust see the \"≈\" prefix instead of a silently-truncated view.",
          "is_bot": false,
          "headline": "test(home): Phase A4 — regression coverage for log-derived metrics + …",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T02:26:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d81acda6ecded925967a168dfb89813fd788a203",
          "body": "Wire dashboard.Cache.Put into activityLoadedMsg (24h window only) and\npostureLoadedMsg so the day-roll history populates without operator\nintervention. Render a \"Δ +X ↑ (1d) / Δ −Y ↓ (7d)\" muted footer on\nboth cards via dashboard.DeltaFor.\n\nCache keys are scoped narrowly (activity-signins-24h /\npost\n[…]\not file stays small — adding more keys later is cheap when a\nmetric proves valuable.\n\nThe footer is silent on first run / fresh cache to keep the layout\nhonest — no permanent \"uncompared\" placeholder.",
          "is_bot": false,
          "headline": "feat(home): Phase A3 — Δ vs 1d/7d footer on Activity + Posture",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T02:23:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cc4af61672af8c0f6fe7ac263cbc868b54ff5188",
          "body": "countPosture fanned out across Administrators / APITokens / GroupRules\n/ Authenticators ports — four management-category list calls per\nrefresh, every one of them rate-limit-billable. On real tenants the\ncard stayed empty because boot-time fan-out exhausted the budget.\n\nReplace with a single 7-day /\n[…]\ns.\n\nPostureMetrics is reshaped end-to-end (no compat shim — only home.go\nread it). renderPostureCard rewrites around severity thresholds with\n\"≈\" prefix when the single-page sample hit logsSampleSize.",
          "is_bot": false,
          "headline": "feat(home): Phase A2 — Posture from a single 7d log walk",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T02:20:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b16f58bdac78ff42d3ca66e6dc59351af1f19749",
          "body": "… count cards\n\nPer-resource count cards (Users/Groups/Apps) couldn't render meaningful\ndata on real tenants — Okta has no public count/aggregate endpoint and\nthe management rate-limit category would throttle a multi-page walk on\nboot. Switch the dashboard's headline to the Logs category, which is a\n\n[…]\nconst\n- drop renderCountCard / Δ row / status row / trend sparkline render helpers\n- drop CardCountsFor + cardLoadedMsg + UsersLoadedForTest helper\n- ActivityLoadedForTest helper added for test parity",
          "is_bot": false,
          "headline": "feat(home): Phase A1 — pivot to System Log metrics, drop per-resource…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-17T02:10:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6551cb2523dfe45ce1b9d4f8f6b2f55866e4d0ac",
          "body": "…no auto-refresh\n\nThe Phase 1-6 dashboard was hammering Okta. Fresh boot fired 8+\npaginated fetches (countUsers, countGroups, countApps, posture\nports × 4, activity × 2 windows, events) and then re-fired all of\nthem every 10s on the auto-refresh tick. Tenants with > a few\nhundred entities exhausted \n[…]\nTest impact:\n  - Existing tests updated for the new empty-state copy + new\n    1h/6h/24h cycle on the Activity card.\n  - windowLabel now prefers hours up to 24 (so \"24h\" reads as\n    \"24h\", not \"1d\").",
          "is_bot": false,
          "headline": "fix(home): rate-limit-safe rewrite — lazy fetch, single-page sample, …",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T13:37:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0502e2040f1cc842b63fd57bfa130900687bd141",
          "body": "Final dashboard polish. The Activity card's secondary window now\ncycles via `t` (7d → 30d → 7d) so operators can flip between\n\"this week vs last week\" without leaving the home screen. Help\noverlay's home entries grow to document the new key + the\nEvents-card jk semantics.\n\ninternal/tui/home/home.go:\n[…]\nts fill in over the next few\nseconds, and can drill into any resource with Enter (or `:users` /\n`:groups` etc. for direct navigation). Esc walks the trail back\nto Home; Esc on Home fires quit confirm.",
          "is_bot": false,
          "headline": "feat(home): Phase 6 — Activity time-window toggle + home test coverage",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T12:20:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2daf5f961b4adafaa8f7bd4fbd6e578e2cb63292",
          "body": "The dashboard's last row now surfaces the 6 most recent\nsecurity-flavored System Log events from the last 6 hours. Each\nrow reads \"12m ago  user.account.lock  alice@acme.com\" and Enter\non a focused event opens Logs scoped to that event's actor (or\ntarget, when no human actor is present).\n\ninternal/t\n[…]\n positive\n    confirmation, not silence.\n\nPhase 6 (next): time-window toggle (`t` cycles 24h / 7d / 30d on\nActivity), light end-to-end tests for the home flow, and home-\nspecific help overlay entries.",
          "is_bot": false,
          "headline": "feat(home): Phase 5 — Recent Critical Events card + per-event drill",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T12:17:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "31035f6a62d2f936fb6531d9c6ceec30b7f6d15c",
          "body": "The three middle rows of the dashboard now render live signals.\n\nActivity card (last 24h · 7d):\n  - Two-window summary in a side-by-side column layout:\n    Sign-ins, Failed sign-ins, Account lockouts, MFA resets,\n    Admin actions, User creates. 24h on the left, 7d on the right.\n  - Failed sign-ins \n[…]\nr).\n\nPhase 5 (next): Recent Critical Events card — last N high-severity\nlog events with Enter to drill into a filter-scoped Logs view.\nPhase 6: time-window toggle (`t` key) + tests for the home cards.",
          "is_bot": false,
          "headline": "feat(home): Phase 4 — Activity / Posture / Health cards",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T12:15:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a19dd77c0cf790dc2883122483d99e7e3d021c13",
          "body": "Each count card now reads \"12,438 · 2m ago / +47 ↑ (7d)  +3 ↑ (1d) /\ntrend ▁▂▃▅▆▇▇█\" — the operator sees the headline number, freshness,\ndirection + magnitude of change vs both windows, and the 14-day\nshape, all in three lines.\n\ninternal/dashboard/delta.go (new):\n  - Delta struct: Current / Previous\n[…]\nes / lockouts /\nMFA resets over 24h + 7d), Posture & Risk card (super-admin count\n/ expiring tokens / inactive users / invalid rules), Health card\n(Okta status / rate-limit headroom / last fetch age).",
          "is_bot": false,
          "headline": "feat(home): Phase 3 — Δ vs 1d / 7d + 14-day trend sparkline on each card",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T12:08:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "970365f97d2cd36c54c2d857ad42c31d13b3ad8d",
          "body": "The Users / Groups / Apps cards now render real numbers. Each card\nfetches its source list (UsersPort / GroupsPort / AppsPort) in a\nbackground tea.Cmd, buckets the result by status / type, and\npersists the snapshot to <UserCacheDir>/ota/dashboard/<tenant>.json\nso the next launch paints instant numbe\n[…]\n    trip completes.\n\nPhase 3 (next): Δ-vs-7d rendering using the History map + sparkline\non the Activity card. Phase 4 wires Activity / Posture / Health\nfetchers. Phase 5 wires Recent Critical Events.",
          "is_bot": false,
          "headline": "feat(home): Phase 2 — count cards + cross-session snapshot cache",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T11:55:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "454528ca553dab0ecd8c8137a574a52e9355a5ea",
          "body": "Boots into the new Okta-admin-style dashboard. Phase 1 ships the\nframe, focus navigation, and routing wire-up — all card bodies are\nplaceholder copy that Phase 2-5 replace with real Okta metrics.\n\ninternal/tui/home (new):\n  - Model with seven focusable cards: Users / Groups / Apps row,\n    Activity \n[…]\nen-specific section + matching\nhelpTitle. Palette golden updated.\n\nPhase 2 (next): wire the Users/Groups/Apps count cards onto the\nexisting Okta ports + a snapshot cache layer for instant first\npaint.",
          "is_bot": false,
          "headline": "feat(home): Phase 1 — scaffold dashboard surface + :home route",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T11:50:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0bf4eb9822e27de91599ae5a4b9a5c806a338835",
          "body": "… install.sh)\n\nSingle-shot release flow built on GoReleaser v2 — tag push (v*) or\nmanual workflow_dispatch fires one CI job that produces:\n\n  - GitHub Releases:  ota_<v>_<os>_<arch>.tar.gz (+.zip for windows)\n                      + checksums.txt + per-archive SBOMs\n  - Homebrew tap:     tedilabs/ho\n[…]\nHUB_TOKEN PAT with repo on tedilabs/homebrew-tap\n\nSee docs/RELEASING.md for the full operator guide — local dry-run,\ntrigger paths, troubleshooting, and end-user install commands for\nREADME inclusion.",
          "is_bot": false,
          "headline": "ci(release): GoReleaser multi-arch pipeline (Releases + Brew + GHCR +…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T07:18:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a68426be3549d9361aac5d9f007629a87d8e7fab",
          "body": "Audit pass on the screen-level navigation stack landed in 4970481\nturned up two semantic gaps:\n\n1. Esc on a drilled frame (e.g. Users → Logs via 'l') was being\n   eaten by the in-screen filter-clear handler instead of popping\n   back to the previous resource. The auto-applied serverFilter\n   from Op\n[…]\nte after drill resets the stack\n  - root with detail open: 1st Esc closes detail, 2nd quits\n\nStale \"nothing to close\" toast comments swept from users.go,\nlogs.go, and the EscapeOpStater interface doc.",
          "is_bot": false,
          "headline": "refactor(app): tighten Esc precedence + cover nav stack with tests",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b0794adeda74c8759889ddade8d91642e28d3f9e",
          "body": "…ory)\n\nSwitches the App Shell from a single m.active Screen to a real\nnavigation stack. The bottom of the stack is the root the operator\nopened with ':' (palette commands replace the entire stack); cross-\nresource drill-downs push frames on top instead of replacing the\nactive screen. Esc with nothin\n[…]\n\n\nHelpers (resetNav / pushNav / popNav / canPopNav) keep the stack\ninvariant 'always non-empty while alive' encapsulated; pushNav is\nidempotent so repeated `l` presses on the Logs frame don't pile up.",
          "is_bot": false,
          "headline": "feat(app): screen-level navigation stack (Android-style activity hist…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "46b5f48344a69f0d79f97a6747828d791e9b5069",
          "body": "Help overlay had drifted from the implementation across several\nsurfaces — this resync pulls every column current with what's wired\ntoday.\n\nGeneral column:\n  - 'R' refresh active screen — was advertised but not bound; now\n    that the global key handler is wired, the entry stays.\n  - Esc reads as \"b\n[…]\ne chrome body budget.\n\nhelpTitle picks up matching headers for the four new screens plus\n\"grouprules\" alongside the existing \"rules\" alias so the help\nmodal opens cleanly from either palette spelling.",
          "is_bot": false,
          "headline": "docs(help): refresh overlay content for v0.2.5 state",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4eecaa71a4691bc0e816777dd999ae4c41cf0114",
          "body": "paletteCommandPool drives the live autocomplete dropdown when the\noperator types ':<text>'. The four resources added in 82296e7\n(network-zones / authorization-servers / api-tokens /\nadministrators) shipped with paletteHints + screenFromName routing\nbut were missing from the pool, so typing ':net' / \n[…]\nrization-server', ':api-token', and\n':administrator'. Plurals + short aliases continue to resolve via\nscreenFromName when the operator types them directly. Also added\n':apilog' which had the same gap.",
          "is_bot": false,
          "headline": "fix(palette): add new resources + apilog to autocomplete pool",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dc254d8307284e0e7e610fca8be799476aaff123",
          "body": "…strators",
          "is_bot": false,
          "headline": "feat: add network-zones / authorization-servers / api-tokens / admini…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9965de8d180a29d7ff49062dca9160dcc9491d24",
          "body": "…panes\n\nTimeline pane now opens with a column header (WHEN · METHOD · PATH ·\nSTATUS · MS) styled muted-bold so it reads as chrome, not data. Each\ndata row aligns to the same canonical column widths (8 / 7 / dynamic\n/ 5 / 8 with 1-cell gutters) so the eye sweeps a stable grid even\nacross mixed paths \n[…]\nll strip styling and re-render\nwith RowCursor — the trade-off is identical to every other detail\nsurface in the TUI. Yank still copies the plain (un-styled) line\ntext so clipboard pastes are pristine.",
          "is_bot": false,
          "headline": "feat(apilog): timeline header + status-class badges + JSON syntax in …",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a1c0d6992afcb85e354597c32b094e4620f55b88",
          "body": "Tab now cycles Timeline → Request → Response → Timeline (Shift-Tab\ngoes the other way). Each pane carries its own shared.BodyCursor so:\n\n- j/k · g/G · Ctrl-d/u  navigate the focused pane independently\n- v / V                 toggles a visual-line selection on each pane\n                        withou\n[…]\nhe same\nPadOrTruncateVisible + RowCursor pipeline the rest of the TUI uses,\nso cursor highlight + visual-line selection look identical to the\ndetail surfaces (Apps / Logs / Groups / Rules / Policies).",
          "is_bot": false,
          "headline": "feat(apilog): three-pane focus + per-pane cursor / visual / yank",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fcc5a559e057c9234c6466f1b6bd94e41ca6183e",
          "body": "The previous fix paused the follow tick while the cursor was off the\nfreshest row, but operators investigating logs at the bottom of the\nbuffer were still firing one /api/v1/logs poll every 10s — 6 calls/min\njust from sitting on the screen, with no opt-in. Stacked on top of\nrange changes, Q queries,\n[…]\nresh) all still work — the operator drives the\ncadence.\n\nStatus badge default flips from [FOLLOW] (on) to [FOLLOW: off]; the\ntest that pinned the old default updated to match the new opt-in\nsemantics.",
          "is_bot": false,
          "headline": "fix(logs): auto-follow OFF by default; rate-limit budget preserved",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0b97198d94e23ec7c98a4badf97ce00ad14a232d",
          "body": "…k fan-out\n\nFour bugs the user surfaced after the API recorder went in:\n\n1. Loading spinner was stuck on its first frame.\n   The chrome's WindowSizeMsg handler forwarded the resize to every\n   child screen but discarded each child's returned Cmd — including\n   the spinner-tick chain a child schedule\n[…]\n call counter so the latch invariant is verified\nwithout depending on the WindowSizeMsg cmd shape. Added an\ninvokeBatch helper that unwraps tea.BatchMsg so cmd-driven tests can\nrun sub-cmds uniformly.",
          "is_bot": false,
          "headline": "fix(app,logs,apilog): spinner, R refresh, log persistence, follow-tic…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2280b642b840b42fb7e2e1597c62e0c846089c49",
          "body": "Adds a session-persistent record of every Okta HTTP round-trip:\n\n- internal/apilog: NDJSON-per-day log under <UserCacheDir>/ota/api,\n  3-day retention with at-startup pruning, plus an in-memory ring of\n  the last 500 entries so the timeline overlay renders instantly.\n  Wraps any base http.RoundTripp\n[…]\nChildIsFiltering so it\n  doesn't fire from inside `/` or `Q` prompts). Also exposed via\n  `:apilog` palette command for keyboards without a tilde.\n\n- Help overlay advertises `~` in the General column.",
          "is_bot": false,
          "headline": "feat(apilog): global Okta API timeline overlay (~ key)",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e59de9a8d89952c0108d85f8cc570955c33caea2",
          "body": "…vy underline\n\nReplaces six per-screen renderXxxTabBar variants with a single\nshared.RenderDetailTabBar(active, width, tk) that returns the\ncanonical 2-line bar:\n\n  Line 1: labels with `▎` accent marker + bold + accent foreground\n          on the active tab; muted foreground on inactive ones\n  Line \n[…]\nfocus path keeps its native `▸`\nmarkers), Users (View + headerStrip used by the column-flow cursor\nsplice). Per-screen tab-bar functions deleted along with their\nunused TabLabels / TabCount variables.",
          "is_bot": false,
          "headline": "feat(tui): polished Pretty/JSON/YAML tab bar with active accent + hea…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2a24c98bbf6f895a07c9abeb49c80121786e0868",
          "body": "…etail body\n\nThree regressions in the just-introduced shared.BodyCursor:\n\n1. Visual mode highlight was rendered with tk.Accent — a foreground-only\n   token. The selected range was effectively invisible against the chrome\n   bg. Switched to tk.RowCursor (matches the Users detail visual-mode\n   render\n[…]\ne is off so the existing\n   linear-cursor flow over Members/Apps/TARGETS stays intact.\n\nThe cursor's Top/Bottom moves were renamed GoTop/GoBottom to free the\nTop identifier for the new viewport field.",
          "is_bot": false,
          "headline": "fix(tui): visible visual highlight + viewport scroll + Ctrl-FBDU on d…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6ab57eec4e8b309a4f57683cc196cf330adca4a1",
          "body": "Wires shared.BodyCursor into Apps / Authenticators / Logs / Groups /\nRules / Policies detail bodies so j/k/g/G/v/V/y all behave like the\nUsers detail (which already had its own cursor). Each detail surface\nnow exposes:\n\n- j/k       — move cursor down / up (clamped to body line count)\n- g/G       — t\n[…]\nbehaviors stay intact.\n\nHelp overlay gains body-cursor entries on every detail context, and\nthe Apps / Logs / Authenticators screen panels mention the detail-\nmode keys alongside their list-mode keys.",
          "is_bot": false,
          "headline": "feat(tui): unified body cursor + visual mode across detail surfaces",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "399e46e539c4bd98f8157c05c22154d33c8f6912",
          "body": "Two related changes:\n\n#F4a — `F` keybind for the Okta `filter=` query parameter. Mirrors\nthe existing `Q` (free-text q=) flow but feeds Okta's structured\nfilter expression syntax (`actor.id eq \"X\" and eventType eq \"...\"`)\nfor precise scoping. Distinct from `Q`:\n- `Q` = free-text search across event \n[…]\nFilter; logs.OpenForQueryMsg →\nOpenForFilterMsg. Each resource's openLogsForCmd accepts a filter\nexpression. The cross-screen handler in the App Shell forwards to\nthe new OpenForFilterMsg.\n\nIssue #F4.",
          "is_bot": false,
          "headline": "feat(logs): server filter param + ID-based l shortcut",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f5008a153567b1cb807a513a7ebeb5441f9162e5",
          "body": "http.Header.Get(\"Link\") returns the FIRST value when a header is\nsent on multiple lines. Okta sometimes splits its pagination Link\ninto separate `Link: rel=\"self\"` and `Link: rel=\"next\"` lines —\nwhich means rel=next was hidden behind rel=self and the page parser\nreturned an empty cursor. Symptom: lo\n[…]\na sends\nmulti-line Link headers to).\n\nLocked the behaviour with a httptest-driven adapter test that emits\ntwo `Link:` headers and asserts SearchPage extracts the rel=next\ncursor.\n\nIssue #F3 follow-up.",
          "is_bot": false,
          "headline": "fix(okta): merge multi-line Link headers before parsing rel=next",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "29e2f61cd6a57077fdec9cb1e2fa9aa8e4902902",
          "body": "When the user pressed `k` on row 0 nothing happened, leaving them\nunsure whether the feature was wired or whether their tenant simply\nhad ≤ limit_per_fetch events in the active window.\n\nAdds two visible diagnostics:\n- Chrome status row gains a \"MORE: k+Enter\" badge (success-toned)\n  whenever canLoad\n[…]\nwindow the operator believes\nshould have more events, the symptom is upstream (Okta returning no\nLink: rel=next or limit-per-fetch covering the full window) rather\nthan a UI bug.\n\nIssue #F3 follow-up.",
          "is_bot": false,
          "headline": "fix(logs): make load-older state visible + explicit no-more feedback",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c35a0b9daad7b1bde2df14252081a37db16f5875",
          "body": "Initial F3 implementation rendered the \"Press Enter to load older\"\nhint above the data but treated cursor=0 as the trigger row. From\nthe operator's perspective the message looked passive — pressing\nEnter on the oldest log row OPENED detail (when no cursor was\nvisible on the message itself), making t\n[…]\nparks on the new row 0\n  (operator immediately sees the older content rather than staring\n  at the same sentinel).\n- Help overlay's Logs section gains the two affordance entries.\n\nIssue #F3 follow-up.",
          "is_bot": false,
          "headline": "fix(logs): make load-older sentinel an actual navigable row",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4a053e719dd59d679733607e596423965c3ca2e4",
          "body": "History fetch previously drained every page Okta returned (limit\n1000, sortOrder=ASCENDING) which was both heavy on the API and\nlossy when an org's last-30-min window held >1000 events.\n\n#F3 v0.2.5 — operator-driven pagination:\n\n- LogsConfig.LimitPerFetch (default 100, configurable). LogsService\n  c\n[…]\ne existing test suite keeps working without per-test rewires.\n- HistoryQuery_DefaultIs30mAscending test renamed + assertion\n  updated to DESCENDING; wire.go threads cfg.Logs.LimitPerFetch.\n\nIssue #F3.",
          "is_bot": false,
          "headline": "feat(logs): configurable limit + DESCENDING fetch with load-older flow",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6147802161e2f8813f76c537a989af7341eae3e0",
          "body": "Follows up bb4f577 which committed the new files but missed the\nworktree changes that wire `l` into the existing screens.\n\n- shared.OpenLogsMsg + logs.OpenForQueryMsg + App Shell handler\n- Users / Groups / Rules / Apps / Authenticators all bind `l` (in\n  list mode AND detail mode) to \"open Logs scop\n[…]\nd; users/hscroll_test now drives KeyRight not the `l`\n  rune\n- padTo body-truncate uses visible-width slicing (fixes modal right\n  border on lines containing wide-cell runes like `→`, `▸`)\n\nIssue #F2.",
          "is_bot": false,
          "headline": "feat(tui): wire `l`-to-Logs across resources + hScroll arrow handlers",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c502ffdaa9e67efca25dad609e0710c09496a427",
          "body": "Two related features that ship together since they touch the same\nkey plumbing.\n\n#F1 — Authenticators resource:\n- domain.Authenticator + AuthenticatorsPort interface\n- okta.AuthenticatorsAdapter (GET /api/v1/authenticators)\n- internal/tui/authenticators screen — list (STATUS / TYPE / KEY /\n  NAME / \n[…]\n tests updated\n  to use Right arrow\n\nBonus: padTo body-truncate now uses visible-width slicing instead of\nthe old byte slice — fixes modal right border when items contain\nwide-cell runes (→, ▸, etc.).",
          "is_bot": false,
          "headline": "feat(authenticators): add Authenticators resource + `l`-to-Logs shortcut",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4625cd96ed410f52d765d596e43e3e80912d0120",
          "body": "Adds shared.Light() — a light-terminal Tokens set with the same\nhue-per-role mapping as Dark (blue accent, green ok, amber warn,\nred danger) but lightness inverted so colours stay legible on\nwhite-background terminals (Solarized-Light / Tomorrow-Light /\nmacOS Default).\n\nshared.ResolveTheme picks the\n[…]\n → Light)\n4. Dark default\n\nshared.PickTheme(ThemeName) is the single entry point used by the\nApp Shell's activeTokens helper. Tests cover the priority order +\nall variants populate Tokens.\n\nAudit U12.",
          "is_bot": false,
          "headline": "feat(theme): Light variant + COLORFGBG / OTA_THEME selector",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c69aa12bc9df4ce9faa409067cefcc6e3c444093",
          "body": "All 5 detail surfaces (Users / Groups / Apps / Rules / Logs)\nduplicated inline modulo arithmetic for Tab/Shift-Tab cycling and a\n4-line if/else block for the `r` raw-toggle. Replaced with the\nexisting shared.NextTab / PrevTab / ToggleRawTab helpers added in\nA4. Apps gained the missing detailRawRetur\n[…]\nthe previous non-JSON tab consistently with the other\nscreens. Groups keeps its own GroupDetailTab type (the 4th Members\nmarker disqualifies a type alias) but converts at the call boundary.\n\nAudit U6.",
          "is_bot": false,
          "headline": "refactor(tui): use shared.NextTab/PrevTab/ToggleRawTab in detail screens",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b8017837ac39a3fa1d2fb74c722f02958353df8c",
          "body": "Action menu, confirm modals, and Help previously stamped the\ncentered modal over entire body rows — leading whitespace from\ncenterInBody overwrote the body cells to the LEFT of the modal,\nand the trailing cells past the modal's right edge were dropped.\nOperators reported the rows underneath the acti\n[…]\nutside the modal\nkeep the body content (rendered Muted+Faint), cells inside carry\nthe modal's own ANSI styling untouched. Result: backdrop is\nvisibly dimmed on every cell except the popup's footprint.",
          "is_bot": false,
          "headline": "fix(tui): splice popup modals into body rows column-wise",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "28660273f2abc101b314ee49acbeaf4f19f63c00",
          "body": "Log Detail surfaced the actor's AlternateID (typically a login like\nalice@acme.com) but you couldn't jump from the event to the user.\nOperators investigating a security event regularly want exactly that\nhop.\n\nAdds Enter on Log Detail → shared.OpenUserDetailMsg keyed on the\nactor's AlternateID (or ID fallback) when the actor is a User. A\nfooter hint surfaces the affordance. Single drill target per event,\nso no focused-cursor mode — Enter is the primary affordance.\n\nAudit U7 / Drill-down gap G5.",
          "is_bot": false,
          "headline": "feat(logs): drill into log event actor on Enter",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8d92cd8d17585d3a1a0f71ccd892c90fe014212a",
          "body": "Rule Detail listed TARGETS as text (`00g_…, 00g_…`) with no way to\ndrill into them. Adds a focused-cursor mode mirroring User Detail's\nextras flow: ] enters focus on the first target, j/k cycle, Enter\nfires shared.OpenGroupDetailMsg to drill into the target's Group\nDetail, [ / Esc exit focus back to\n[…]\nname via the existing groupNames cache\nwhen available. A footer hint surfaces the affordance (\"] focuses\nTARGETS for drill-down\" / \"Enter to drill into focused target\").\n\nAudit U7 / Drill-down gap G4.",
          "is_bot": false,
          "headline": "feat(rules): drill into Rule Detail target groups via focused cursor",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "39e837bcacc0a7c3fcd026db0c074dcd971d4f19",
          "body": "Group Detail's extras boxes (Members + Apps) had no Enter handler,\nso the keypress fell through to the outer list-mode handler which\nsilently re-opened the same row's detail and reset the lazy-fetch\ncaches — operators reported pressing Enter and seeing nothing\nuseful while the boxes flickered to \"lo\n[…]\n the new shared.OpenUserDetailMsg), the\nrest drills to App Detail (#G3, reuses shared.OpenAppDetailMsg).\nDetail-open without extras focus is now an explicit no-op.\n\nAudit U7 / Drill-down gaps G1 + G3.",
          "is_bot": false,
          "headline": "fix(groups): drill into focused Members/Apps row on Enter",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d51871dd5761a18fe23f75c81f16971d46a3735f",
          "body": "Adds the Users counterpart of OpenGroupDetailMsg / OpenAppDetailMsg\nso other screens can drill into user detail by ID. Users list owns\nthe OpenDetailByIDMsg handler directly (no Wrapper indirection).\nfetchUserByIDCmd surfaces errors instead of silently constructing a\nplaceholder user, mirroring apps.fetchAppByIDCmd.\n\nFoundation for upcoming Group Detail Members and Log Detail actor\ndrill-downs (#G2, audit U7).",
          "is_bot": false,
          "headline": "feat(shared): OpenUserDetailMsg + Users drill-down handler",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1580fe84289000febfc7c9b40176dded5643bc68",
          "body": "- Extract actions, probes, toast helpers from app.go into dedicated files\n- Consolidate ToastMsg/ToastLevel into shared so any TUI package can emit\n- Replace inline detailToast/statusToast slots with floating toast band\n- Add ActionFailedMsg broadcast + RowDanger flash on destructive failures\n- Add \n[…]\nlp palette command, Palette column in help overlay\n- Distinguish `/` client filter (cyan) from `Q` server query (yellow) prompts\n- Add testfx.Seeded{Users,Groups,Apps}Port to replace inline test fakes",
          "is_bot": false,
          "headline": "refactor(app): unify toast pipeline and split shell into focused files",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3b18796e884a94089b6021dd14bbe1232fedb735",
          "body": "… spinners\n\n- add status.okta.com probe + title-bar segment with /me reachability fallback (#190 #198)\n- flash RowChanged on refresh-detected diffs across users/groups/rules/apps (#193 #202)\n- show loading spinner placeholder before first fetch (#194)\n- replace inline confirm bands with centered mod\n[…]\ntored (#192)\n- promote Group Detail Members to side-by-side Members+Apps boxes (#189)\n- accept Enter as confirm key on quit/action prompts (#200)\n- extract shared scrollbox, spinner, highlight helpers",
          "is_bot": false,
          "headline": "feat(tui): v0.2.4 chrome status, refresh flash, modal popups, loading…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2329ce0963a6c0dbda98c88d64adff44b09d90f5",
          "body": "…ts across follow ticks\n\nFive issues from the v0.2.1 follow-up review:\n\n1. (#186) Logs `Q` server query persists across the auto-refresh\n   tick. Previously each tick built LogsQuery with empty Q, so the\n   committed query \"disappeared\" after pollInterval seconds. Now\n   followFetchCmd snapshots m.q\n[…]\nea.Tick reschedule so a 10s\n  pollInterval doesn't block the test goroutine.\n- All in-tree UsersPort / GroupRulesPort fixtures + fakes grow\n  the new method stubs.\n\ngo test ./... + go vet ./... clean.",
          "is_bot": false,
          "headline": "feat(actions): expand User + Group Rule lifecycle ops; Q query persis…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0895a061a338bb37672e57d8defd5ff61e6a7b14",
          "body": "…e log search\n\nThree issues from the v0.2.0 follow-up review:\n\n1. (#183) Detail yank includes ANSI codes (FIX)\n   y on the Pretty / JSON / YAML body wrote the rendered lines to\n   the clipboard verbatim — operators pasted `\\x1b[38;5;…m\"login\":\\x1b[0m`\n   into their notes. shared.StripCSI before clip\n[…]\nistory fetch carries the\n       current query.\n\nTests: yank_strip_test.go (StripCSI behaviour), server_query_test.go\n(Q→type→Enter path + Esc-clears-query). All `go test ./...` +\n`go vet ./...` clean.",
          "is_bot": false,
          "headline": "fix+feat(tui): plain-text yank, full-row Groups highlight, server-sid…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ff5a899bb014ed56c40debbee02690e44bdb6cae",
          "body": "- extract shared.RenderRowCursor for users/groups/apps/rules/policies/logs\n- add shared.MountModal with tone slots; route action menu through it\n- add shared.PlaceholderRow / LoadingRow / EmptyRow / ErrorRow helpers\n- consolidate group members, policy rules, user detail extras placeholders",
          "is_bot": false,
          "headline": "refactor(tui): unify cursor row pipeline + modal/empty helpers",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6c83a3aedff5ac689a2d0b3082dd95308ec7e18a",
          "body": null,
          "is_bot": false,
          "headline": "feat(ui): v0.2.0 chrome status row + unified mode badges",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5180577eb9a0399b6301f60769e30f6135da8e76",
          "body": "Issue #180 — Groups/Apps box right border misaligned. The\nrenderScrollBox content area reserved 4 cells (`width-4`) but each\nbody row composed `│ + space + row + space + bar + │` = 5 reserved\ncells, so every row rendered 1 cell wider than the top/bottom\nborders. The `╮` of the right box also wasn't \n[…]\nlits into renderPrettyWithColumnCursor + renderFlatLineCursor.\nJSON / YAML tabs keep the original flat per-line cursor.\n\nTests: column_flow_test.go (3 cases). Goldens unchanged — output\nshape matches.",
          "is_bot": false,
          "headline": "fix(users): detail box border + column-flow cursor",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e4707aa62d524dfe6425f110d78962aa2b5257eb",
          "body": "Removes the per-user Groups/Apps count columns added in v0.1.16\nalong with their lazy-fetch plumbing:\n  - usersColumnSpecs / renderUsersHeader / renderUsersRow / observedColumnWidths\n  - groupCounts / appCounts / countsLoaded state\n  - userCountLoadedMsg, kickUserCountFetches, fetchUserCountsCmd\n\nWh\n[…]\nxes, which fetch lazily only when the operator opens the\ndetail surface (1 user × 2 calls instead of 200 × 2).\n\nGoldens regenerated; usersSortColumnIdx reverted to the 8-column\nlayout from issue #145.",
          "is_bot": false,
          "headline": "revert(users): drop GROUPS/APPS columns to stop API call burst",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b05e86778607a4af656cf66c35a929d844474315",
          "body": "Operators reported the auto-refresh re-emitting rows that history\njust delivered. Two race paths can produce overlapping batches:\n\n  1. Init's tea.Batch fires fetchHistoryWindowCmd AND the first\n     follow tick concurrently. On a slow API the tick lands while\n     history is still resolving — its c\n[…]\n — independent of since\nprecision and sort order — so dedup before append is the safest\nfix. Also bumps the default poll interval to 10s to align with\nconfig defaults and reduce baseline API pressure.",
          "is_bot": false,
          "headline": "fix(logs): dedupe follow-mode events by UUID + raise default to 10s",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c0557b9c267d4e60e78c3cd3349a29f6b702469f",
          "body": null,
          "is_bot": false,
          "headline": "v0.1.16: filter-mode runes, refresh ticks, divider stamps, user counts",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2601482df4da7ee2899e0593242ca03f4c899649",
          "body": "Issue #171 — pressing Enter on the focused Groups or Apps box in\nUser Detail now lands on the matching screen with that resource's\ndetail surface already open. The shell hops screens AND forwards an\ninternal OpenDetailByIDMsg so the destination list fetches the\ntarget by ID and surfaces detail mode \n[…]\n by the Wrapper (regardless of\n  mode) and infers AppType from the fetched App's SignOnMode so\n  ScreenApps lands on the right type.\n- User Detail key hint footer + `?` overlay list the new shortcuts.",
          "is_bot": false,
          "headline": "feat(users): User Detail Enter on Groups/Apps row drills into resource",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b711b16e77394e87db39065992d2e647d67a6731",
          "body": "User asked for the assigned-groups + assigned-apps lists to render\nas separate boxes (left=Groups, right=Apps) below the info grid,\neach with its own scrollbar, and for the chrome's top border to\nstay put even when the lists are long.\n\nImplementation:\n\n  - ListModel grew detailFocus / detailGroupsCu\n[…]\nerminal top; now it stops at the budget and the box's own\nscrollbar covers the rest.\n\nHelp E2E test fixture: now seeds a 120x36 WindowSizeMsg so the\nhelp modal (~20 rows) fits inside the new body cap.",
          "is_bot": false,
          "headline": "feat(users): User Detail Groups/Apps as side-by-side scrollable boxes",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ad26ee02751f29cfe18155cc7217884a138dfb06",
          "body": "User reported \"할당된 Apps 정보가 조회가 안됨\" — every user's Apps\nsection showed \"apps failed: …\" instead of the assigned-apps list.\nRoot cause: my wireAppLink struct had `credentialsSetup` typed as\n`string`, but Okta returns it as a `bool`, so json.Unmarshal failed\non the very first array entry and the whole\n[…]\n rather than the per-user link.\n\nAdds a regression test that decodes a real Okta-shaped payload\n(with `credentialsSetup: false`) end-to-end and verifies the\nmapping picks appInstanceId for AppLink.ID.",
          "is_bot": false,
          "headline": "fix(okta): User Detail Apps fetch — credentialsSetup is bool, not string",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "53deb43a0397e18b31752261a3b012bf81cbb5aa",
          "body": "User asked for the detail view to surface \"this person is in these\ngroups and sees these apps\" without leaving the screen. The Pretty\ntab now grows two sections beneath the 2-col layout:\n\n  ── Groups ─────────────────────────────────\n    [OKTA_GROUP]  Engineering\n    [BUILT_IN]    Everyone\n\n  ── App\n[…]\ne UI never goes blank during the fetch and operators see why\na section is empty.\n\nAdds detail_extras_test pinning the open → batched fetches → loaded\nsections flow with both groups and apps populated.",
          "is_bot": false,
          "headline": "feat(users): User Detail — assigned Groups + Apps sections",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "712b40d9c2cde5f24d859b31f8a52c6e0257521a",
          "body": "…ient-side\n\nUser reported live \"Failed to load Apps · Request rejected by Okta\"\non every per-type apps view. Root cause: the adapter sent\n`filter=signOnMode eq \"SAML_2_0\"` to /api/v1/apps. The Apps endpoint's\nfilter parameter only accepts the predicates Okta documents\n(user.id / group.id / name) — s\n[…]\netter to be slightly slower\nthan to have the screen permanently broken.\n\nAdds two adapter tests pinning (a) the wire request must NOT carry\nsignOnMode and (b) the no-type path still returns every row.",
          "is_bot": false,
          "headline": "fix(okta): Apps API rejected signOnMode filter — move type scoping cl…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5847ab4352ba1b6e4b3dc51ba26e22abd3eb7020",
          "body": "User reported the apps view \"doesn't work at all\" — these regression\ntests assert the four primary entry paths actually render the\nexpected screen end-to-end through the App Shell:\n\n  - `:app<Enter>`        → picker renders, divider says `apps`.\n  - `:apps<Enter>`       → picker (plural alias).\n  - \n[…]\nhe live binary still misbehaves, the failure is\neither tenant-specific (auth scope, payload shape) or downstream\nof the routing path. Awaiting a specific repro from the user\nbefore changing live code.",
          "is_bot": false,
          "headline": "test(apps): pin :app / :saml-app / :apps / picker-nav routing",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "11ae62daafaa8e450b0ecf294e53fce3ebdbe8a2",
          "body": "Mirrors the Policies pattern (issue #154 + #165) for Okta\nApplications. Operators can drill from a type picker (SAML 2.0 /\nOIDC / Bookmark / SWA / SCIM / Other) into a typed list, or jump\nstraight to a typed list via the palette:\n\n  :app             → picker\n  :saml-app        → SAML 2.0 list\n  :oid\n[…]\nloses the v0.1.12 cycle:\nstatus icon drop, tight-fit port to all lists, log column reorder,\narrow keys, Group TYPE labels, MEMBERS / EXPRESSION / TARGETS-by-\nname, palette `:group-rule` autocomplete).",
          "is_bot": false,
          "headline": "feat(apps): Apps screen — type-select wrapper + per-app-type palette",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b6d2eb443ab2bb366a9c45a834309ddd6e0c99de",
          "body": "Operators that know which policy kind they want shouldn't have to\nenter the type-select picker every time. Issue #165 wires direct\npalette routes for every supported PolicyType:\n\n  :okta-sign-on        → OKTA_SIGN_ON\n  :access-policy       → ACCESS_POLICY\n  :password-policy     → PASSWORD\n  :mfa-enr\n[…]\nnonicals so all\n    the routes surface as suggestions.\n\nAdds a regression test that types `:okta-sign-on<Enter>` end-to-end\nand asserts the picker doesn't render while the seeded typed-list\ndata does.",
          "is_bot": false,
          "headline": "feat(policies): per-type palette routes skip the picker",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7955683031988f8010b76cc30c444bea02e4cab0",
          "body": "Issues #157 + #158: Logs and Policies were the last two lists still\nusing hand-rolled padding switches keyed off arbitrary breakpoints\n(80/90/100/120). Different rows ended up with different effective\ncolumn widths, the chrome's right border drifted, and adding a\ncolumn meant editing five conditiona\n[…]\n PUBLISHED + SEV + MESSAGE\nstay visible longest.\n\nTest updates: golden_test column-header assertion takes the new\n8-column lineup; list_flow_test asserts DisplayMsg's \"User login\"\nsurfaces in MESSAGE.",
          "is_bot": false,
          "headline": "feat(logs+policies): port to shared column-spec system; logs reorder",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9aa86e86ad1bf19d89d3e08208afd5e3ec863b3e",
          "body": "…me-resolved TARGETS\n\nFour coupled v0.1.12 user requests:\n\n#160 Group TYPE column shows OKTA / APP / BUILT_IN (was [O]/[A]/[B]).\n#161 Group list grew a MEMBERS column. domain.Group.MemberCount\n     (*int) populates from _embedded.stats.usersCount when the okta\n     adapter passes expand=stats. nil r\n[…]\nt ID\n     through GroupsPort.Get and stores into the cache. Falls back\n     to the raw 00g_… ID when resolution fails.\n\nSpec orderings updated, headers + drop priorities adjusted; goldens\nregenerated.",
          "is_bot": false,
          "headline": "feat(groups+rules): TYPE labels, MEMBERS count, EXPRESSION column, na…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "087600f11197a07aea027a454a35019982b16d39",
          "body": "Issue #156 — the row's bg tint (issue #155) already telegraphs an\nabnormal status, and the `[+]`/`[!]`/`[X]` mono prefix doubled the\ncolumn width without adding signal. StatusBadge.Render() now emits\nthe bare LABEL only.\n\nColumnSpec grew an AlignCenter option so the STATUS column reads as\na centered\n[…]\n; Policies + Logs still use manual\npadding pending the #157 port to the column-spec system.\n\nTest updates: rules sort assertion no longer matches \"[+] ACTIVE\" —\nsubstring is now centered \"  ACTIVE  \".",
          "is_bot": false,
          "headline": "feat(tui): drop STATUS icon, center-align the column",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2ac1d52b43a3cb48f24aad258bd6b9c18713750e",
          "body": "Two small UX fixes from the v0.1.12 user feedback:\n\n#159 — Arrow keys now match the Vim-style h/j/k/l everywhere. A new\nshared.NormalizeArrowKey helper rewrites tea.KeyUp/Down/Left/Right\ninto their rune equivalents at the top of each list's keypath, so\nthe existing rune switch handles both. Logs gua\n[…]\ncarried `rule` (the screen's short\nname), which made `:gr` produce just `group` and operators\nexpecting `group-rule` saw nothing. Test updated to assert both\ncanonicals appear and plurals stay hidden.",
          "is_bot": false,
          "headline": "feat: arrow keys work alongside h/j/k/l + palette gets :group-rule",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8c701bd2d7f9d40b6c6c1e4adcc7211fca430295",
          "body": "User asked for an at-a-glance signal when a row's status isn't a\n\"normal\" state. The status badge already conveys this in a single\ncell, but a glance at a wide list shouldn't require scanning every\ncell — the whole row's background should pop.\n\nThree new tokens (Tokens.RowDanger / RowWarning / RowMu\n[…]\nht\nfix from #146).\n\nPinned the mapping in users tests; integration-byte testing is\nconstrained by NO_COLOR=1 in tests (lipgloss stops emitting ANSI),\nso the regression check is at the helper boundary.",
          "is_bot": false,
          "headline": "feat(tui): tint entire row bg when STATUS is abnormal",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1aef3b9f7687f0845797aef860df252276fda59f",
          "body": "Wraps the v0.1.10 cycle:\n  - Palette / filter overlay's right border now sits flush against\n    the chrome's right border (lipgloss .Width semantics fix).\n  - Logs status line: labelled `range … · tail … · follow …` with\n    colour-coded toggles, plus an inline shortcut hint so the keys\n    are disc\n[…]\nng on\n    eventType / actor / outcome / IP, mirroring the other lists.\n  - Policies — Wrapper handles type-select → list transition; the\n    inline detail lazy-fetches rules and renders them per-type.",
          "is_bot": false,
          "headline": "chore(release): bump to v0.1.10",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6f6c6f44dbf0dd39c8a748484009eae94ecc0a91",
          "body": "The Policies screen had a TypeSelectModel that reported its pick\nvia Picked() but no caller actually transitioned to the ListModel —\noperators got stuck on the type menu. Issue #154 wires the full\nflow:\n\n  - new Wrapper model owns the type-select → list transition. The\n    App Shell mounts the Wrapp\n[…]\ntches fresh.\n\nHelp modal grew the matching entries on the policies screen.\nWrapper test pins the three flows: TypeSelect → List, Esc back to\nTypeSelect, and Enter on a list row firing the Rules fetch.",
          "is_bot": false,
          "headline": "feat(policies): per-type list/detail flow + lazy rules fetch",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e6409e3112d1ffee9b364a813f850e3104f0d824",
          "body": "Two coupled improvements the user asked for:\n\n#152 — Tail / Follow / time-range controls were \"invisible\" in the\nsense that the existing `[TAIL OFF] [FOLLOW]` indicator didn't make\nclear what flipping `s` / `f` actually did, and the time-range\nshortcuts (0/1/3/c/e) were undocumented. The body status\n[…]\n the logs screen\n(/ + s + f + r + 0/1/3/c/e + Enter / d). Existing list-flow tests\nupdated for the new \"tail ON\" / \"follow ON\" status form; the\nlegacy `tailIndicator` survives for any external caller.",
          "is_bot": false,
          "headline": "feat(logs): `/` filter + visible tail/follow/range status line",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "24dee05e2ff331226c1b4424c9c59516922ff1fd",
          "body": "User reported the floating palette / filter box's right edge looked\nbroken — its `╮` corner / right border didn't line up with the\nchrome's right `│` border (issue #151).\n\nRoot cause: lipgloss `.Width(N)` sizes the inside-of-border area\n(padding included), so the rendered outer box is N+2 cells wide\n[…]\ninst the chrome's right\nborder.\n\nAdds a regression test that opens `:`, finds the palette's top\nborder line, and asserts there are no stray `─` characters\nbetween the modal's `╮` and the chrome's `│`.",
          "is_bot": false,
          "headline": "fix(app): palette / filter modal right border aligns with chrome",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1544fc8603dde07739c64753c324b14f4a6f4da8",
          "body": "Wraps the v0.1.9 cycle:\n  - Help modal: full terminal width, 3-column layout with `│`\n    separators, rune-aware padding so multibyte header rules align.\n  - Users list: rune-width math now honours East-Asian-Wide / emoji\n    glyphs (go-runewidth), so a Korean nickname no longer drifts the\n    chrom\n[…]\nop-left.\n  - Palette autocomplete: surfaces only the singular canonicals\n    (user / group / rule / policy / log / unmask / mask / quit /\n    reset-* / unlock); plurals + aliases still ROUTE on enter.",
          "is_bot": false,
          "headline": "chore(release): bump to v0.1.9",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "721c374f236d48f4c40c7684a4cfb70dcc84b02d",
          "body": "…ging)\n\nUser reported the detail view felt cramped — content rolling to the\ntop-left corner of a wide terminal, the 2-column layout fixed at\n56-cell sections regardless of how much screen room was available.\n\nAdds pickPrettySectionWidth(termWidth) to size each section to the\ncurrent chrome (split in\n[…]\nh the\nListModel now refreshes from m.width on every newDetail() so the\nlive WindowSizeMsg flows through. Section headers now extend across\nthe full available row, sections-side-by-side fills the body.",
          "is_bot": false,
          "headline": "fix(users): User Detail Pretty fills terminal width (no top-left clin…",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "924d573629a84f374987c328234f9ff9127d6903",
          "body": "User reported the help modal was cramped: rows too short, columns\ntoo close together, hard to scan. Restructured the layout for issue\n#147:\n\n  - HelpModel now has a width field set via WithWidth(); the App\n    Shell pipes the chrome's content width in so the modal fills\n    most of the screen instea\n[…]\nhe next column and shift the separator.\n\nThe App Shell hands modalWidth = chrome contentWidth - 4 (small\nbreathing-room gutter), clamped to 60 cells minimum so a tiny\nterminal still renders something.",
          "is_bot": false,
          "headline": "refactor(help): full-width 3-column layout with clear separators",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "92e50e62de8018cb405664aa0e099e7bbefba661",
          "body": "User reported the autocomplete list was noisy with `grouprules`,\n`grouprule`, `group-rule`, `group-rules`, `group_rule`, `group_rules`,\n`gr`, `users`, `policies`, etc. cluttering every prefix. Trimmed the\nsuggestion pool to the five singular canonicals — user / group /\nrule / policy / log — plus the\n[…]\ngression tests: one pins that `:gr` surfaces only `group`\n(not the seven plural variants); the other sweeps `:us` / `:po` /\n`:lo` and asserts the singular canonical surfaces while plurals\nstay hidden.",
          "is_bot": false,
          "headline": "feat(app): palette autocomplete surfaces only singular canonicals",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "efcfa277ba66acbc1b315fd35b01abc9d2c573c9",
          "body": "User reported \"Users 목록 창에서 Login이나 Nickname이 긴 경우 해당\n행이 깨지는 것 같음. 오른쪽 테두리도 어긋나있고.\" The root cause\nisn't long ASCII — that case works. It's CJK / emoji glyphs:\n\n  alice@acme.com  Bob       (each char = 1 cell, 1 rune)\n  alice@acme.com  박병진    (each Hangul = 2 cells, 1 rune)\n\nThe previous visibleWidt\n[…]\n every body line of a Users list\nwith a Korean nickname measures to the same visible width as the\nheader and the ASCII row. Locks the alignment so a future width\nimplementation can't silently regress.",
          "is_bot": false,
          "headline": "fix(shared): width math honours East-Asian-Wide / emoji rune widths",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f87fd5ce7b1bbda888c311a84660eae34cf78137",
          "body": "Wraps the v0.1.8 cycle:\n  - User Detail Pretty: 2-column layout (Identity/Org/Status |\n    Contact/Address/Custom).\n  - User Detail: Contact / Address field ordering pinned;\n    timezone moves from Contact to Address.\n  - Users list: column reorder (LOGIN > NICKNAME > DIVISION >\n    DEPARTMENT > TIT\n[…]\nax(header, observed) so columns no longer truncate observed\n    data.\n  - Detail JSON/YAML: cursor row highlight strips inner ANSI so the\n    bg tint extends across values too — not just column names.",
          "is_bot": false,
          "headline": "chore(release): bump to v0.1.8",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4fa34ad88fe1ffd6370a8116a112a79abb0c5af0",
          "body": "Three coupled changes per issues #143 + #144:\n\n1. Two-column layout (#143):\n\n     ── Identity ──────  ── Contact ──────\n     login    alice…     mobilePhone  +1-…\n     email    alice…\n     firstName Alice    ── Address ──────\n     ...                state    WA\n                        city     Seatt\n[…]\n-by-side\n    composition didn't regress.\n  - OrganizationFixedOrder simplified to assert relative key order\n    in the rendered string (slice-based check no longer works with\n    interleaved columns).",
          "is_bot": false,
          "headline": "feat(users): Pretty View — 2-column layout + Address/Contact reordering",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "29cf732482b73b507cfb51994ff164268636e722",
          "body": "User pinned the lineup in #145:\n\n  LOGIN > NICKNAME > DIVISION > DEPARTMENT > TITLE > STATUS >\n  LAST LOGIN > LAST UPDATED\n\nEMPLOYEE# dropped, LAST UPDATED added (rendered from User.LastUpdated\ninstead of StatusChanged). Drop priorities degrade right-to-left so\nLOGIN + STATUS stay essential.\n\nAlso f\n[…]\ntions now reference LOGIN as leftmost (was STATUS).\n  - help_e2e: cursor regexp simplified — STATUS isn't directly to\n    the right of the cursor anymore.\n  - chrome / users / app goldens regenerated.",
          "is_bot": false,
          "headline": "fix(users): list column reorder + observed-fit + drop EMPLOYEE#",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4a0fc1a25c69b7c41b29a61f7f12bb91a14a12f9",
          "body": "Wraps the v0.1.7 cycle:\n  - Logs Detail: Pretty / JSON / YAML tabs + Tab / r shortcuts.\n  - Logs: f / r / s shortcuts produce visible state changes.\n  - Chrome: count + filter stamped into the upper divider next to\n    the resource label.\n  - Lists: bold + accent column header rows.\n  - Users: tight\n[…]\non in\n    spec-mandated fixed order.\n  - Detail JSON / YAML: row highlight extends across full row.\n  - Groups: dropped the bogus TAGS column.\n  - Group Detail: Members tab + `m` shortcut, lazy fetch.",
          "is_bot": false,
          "headline": "chore(release): bump to v0.1.7",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2694e1ddc398efec663769faa5b839e7960f7b4c",
          "body": "The user asked for the same 3-tab structure that Users / Groups /\nGroup Rules already have on their detail surfaces (issue #135). Log\ndetail had only the curated Pretty body — no way to pull the full\nevent payload without cracking open the API console.\n\nAdds:\n\n  - LogDetailTab enum (Pretty / JSON / \n[…]\nab decodes the same payload and re-emits at 2-space\n    indent, syntax highlighted via shared.HighlightYAML.\n\nHelp modal grew the matching entries on the log-detail screen\n(Tab / Shift-Tab / r / Esc).",
          "is_bot": false,
          "headline": "feat(logs): Pretty / JSON / YAML tabs on Log Detail",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a08946e1c0447c23d4c6b7527f3c5358e7233e61",
          "body": "Three changes the user spec'd in issue #140:\n\n1. Status section moves to the TOP and grows lifecycle timestamps —\n   created / activated / statusChanged / lastLogin / lastUpdated /\n   passwordChanged. The status badge stays the lead row. Empty\n   timestamps drop out so a sparse user doesn't render p\n[…]\nnothing-but-name user doesn't render six headers with blank bodies.\n\nAdds a regression test pinning Organization's canonical sequence and\nextends the existing grouping test for the new section layout.",
          "is_bot": false,
          "headline": "feat(users): Pretty View — Status on top, Address split, fixed Org order",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "77093827fe0ec4e10453e631bc0d6735f5680ad4",
          "body": "User reported \"Detail View의 JSON / YAML에서 선택 행 하이라이트가\n컬럼 이름까지만 동작해. 전체 행에 대해 동작해야겠지?\" — the cursor\nrow's bg tint stopped at the line's actual character count, so a\nshort JSON line like `  \"id\":` got highlighted but everything to the\nright of the colon stayed flat.\n\nLipgloss `.Render(line)` only colo\n[…]\nidth\nregardless of which row the cursor's on.\n\nThe pad width is the maximum visible width of the SELECTABLE body\n(skipping the 3 header rows) so the highlight surface stays stable\nas the cursor moves.",
          "is_bot": false,
          "headline": "fix(users): row highlight extends across full Detail JSON/YAML row",
          "author_name": "Byungjin Park",
          "author_login": "posquit0",
          "committed_at": "2026-06-15T06:35:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 2,
      "commits_last_year": 156,
      "latest_release_at": "2026-07-20T07:08:26Z",
      "latest_release_tag": "v0.1.2",
      "releases_from_tags": false,
      "days_since_last_push": 8,
      "active_weeks_last_year": 6,
      "days_since_latest_release": 8,
      "mean_days_between_releases": 0
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 87,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/tedilabs/ota",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/tedilabs/ota",
          "is_deprecated": false,
          "latest_version": "v0.1.2",
          "repository_url": "https://github.com/tedilabs/ota",
          "versions_count": 2,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-07-20T07:05:37Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 8
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 2,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 7
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": true,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 119335,
      "source_files_sampled": 278,
      "oversized_source_files": 2,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "go.mod"
      ],
      "advisories": {
        "error": null,
        "scope": "repository_graph",
        "source": "osv",
        "findings": [
          {
            "name": "golang.org/x/sys",
            "direct": false,
            "version": "v0.38.0",
            "severity": "unknown",
            "ecosystem": "go",
            "cvss_score": null,
            "advisory_ids": [
              "GO-2026-5024"
            ],
            "fixed_version": "0.44.0",
            "advisory_count": 1,
            "oldest_advisory_days": 66
          },
          {
            "name": "golang.org/x/text",
            "direct": false,
            "version": "v0.28.0",
            "severity": "unknown",
            "ecosystem": "go",
            "cvss_score": null,
            "advisory_ids": [
              "GO-2026-5970"
            ],
            "fixed_version": "0.39.0",
            "advisory_count": 1,
            "oldest_advisory_days": 13
          }
        ],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {
          "unknown": 2
        },
        "advisory_count": 2,
        "affected_count": 2,
        "assessed_count": 41,
        "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/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": "github.com/charmbracelet/x/ansi",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.11.6"
        },
        {
          "name": "github.com/charmbracelet/x/exp/teatest",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.0.0-20260422141420-a6cbdff8a7e2"
        },
        {
          "name": "github.com/google/uuid",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.6.0"
        },
        {
          "name": "github.com/knadh/koanf/parsers/yaml",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.1.0"
        },
        {
          "name": "github.com/knadh/koanf/providers/file",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.2.1"
        },
        {
          "name": "github.com/knadh/koanf/v2",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.3.4"
        },
        {
          "name": "github.com/mattn/go-runewidth",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.0.19"
        },
        {
          "name": "github.com/muesli/termenv",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.16.0"
        },
        {
          "name": "github.com/stretchr/testify",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.11.1"
        },
        {
          "name": "gopkg.in/natefinch/lumberjack.v2",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.2.1"
        },
        {
          "name": "gopkg.in/yaml.v3",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v3.0.1"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "github.com/atotto/clipboard",
            "direct": true,
            "version": "v0.1.4",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/bubbletea",
            "direct": true,
            "version": "v1.3.10",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/lipgloss",
            "direct": true,
            "version": "v1.1.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/x/ansi",
            "direct": true,
            "version": "v0.11.6",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/x/exp/teatest",
            "direct": true,
            "version": "v0.0.0-20260422141420-a6cbdff8a7e2",
            "ecosystem": "go"
          },
          {
            "name": "github.com/google/uuid",
            "direct": true,
            "version": "v1.6.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/knadh/koanf/parsers/yaml",
            "direct": true,
            "version": "v1.1.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/knadh/koanf/providers/file",
            "direct": true,
            "version": "v1.2.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/knadh/koanf/v2",
            "direct": true,
            "version": "v2.3.4",
            "ecosystem": "go"
          },
          {
            "name": "github.com/mattn/go-runewidth",
            "direct": true,
            "version": "v0.0.19",
            "ecosystem": "go"
          },
          {
            "name": "github.com/muesli/termenv",
            "direct": true,
            "version": "v0.16.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/stretchr/testify",
            "direct": true,
            "version": "v1.11.1",
            "ecosystem": "go"
          },
          {
            "name": "gopkg.in/natefinch/lumberjack.v2",
            "direct": true,
            "version": "v2.2.1",
            "ecosystem": "go"
          },
          {
            "name": "gopkg.in/yaml.v3",
            "direct": true,
            "version": "v3.0.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/aymanbagabas/go-osc52/v2",
            "direct": false,
            "version": "v2.0.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/aymanbagabas/go-udiff",
            "direct": false,
            "version": "v0.3.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/colorprofile",
            "direct": false,
            "version": "v0.4.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/x/cellbuf",
            "direct": false,
            "version": "v0.0.15",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/x/exp/golden",
            "direct": false,
            "version": "v0.0.0-20241011142426-46044092ad91",
            "ecosystem": "go"
          },
          {
            "name": "github.com/charmbracelet/x/term",
            "direct": false,
            "version": "v0.2.2",
            "ecosystem": "go"
          },
          {
            "name": "github.com/clipperhouse/displaywidth",
            "direct": false,
            "version": "v0.9.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/clipperhouse/stringish",
            "direct": false,
            "version": "v0.1.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/clipperhouse/uax29/v2",
            "direct": false,
            "version": "v2.5.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/davecgh/go-spew",
            "direct": false,
            "version": "v1.1.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/erikgeiser/coninput",
            "direct": false,
            "version": "v0.0.0-20211004153227-1c3628e74d0f",
            "ecosystem": "go"
          },
          {
            "name": "github.com/fsnotify/fsnotify",
            "direct": false,
            "version": "v1.9.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/go-viper/mapstructure/v2",
            "direct": false,
            "version": "v2.4.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/knadh/koanf/maps",
            "direct": false,
            "version": "v0.1.2",
            "ecosystem": "go"
          },
          {
            "name": "github.com/lucasb-eyer/go-colorful",
            "direct": false,
            "version": "v1.3.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/mattn/go-isatty",
            "direct": false,
            "version": "v0.0.20",
            "ecosystem": "go"
          },
          {
            "name": "github.com/mattn/go-localereader",
            "direct": false,
            "version": "v0.0.1",
            "ecosystem": "go"
          },
          {
            "name": "github.com/mitchellh/copystructure",
            "direct": false,
            "version": "v1.2.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/mitchellh/reflectwalk",
            "direct": false,
            "version": "v1.0.2",
            "ecosystem": "go"
          },
          {
            "name": "github.com/muesli/ansi",
            "direct": false,
            "version": "v0.0.0-20230316100256-276c6243b2f6",
            "ecosystem": "go"
          },
          {
            "name": "github.com/muesli/cancelreader",
            "direct": false,
            "version": "v0.2.2",
            "ecosystem": "go"
          },
          {
            "name": "github.com/pmezard/go-difflib",
            "direct": false,
            "version": "v1.0.0",
            "ecosystem": "go"
          },
          {
            "name": "github.com/rivo/uniseg",
            "direct": false,
            "version": "v0.4.7",
            "ecosystem": "go"
          },
          {
            "name": "github.com/xo/terminfo",
            "direct": false,
            "version": "v0.0.0-20220910002029-abceb7e1c41e",
            "ecosystem": "go"
          },
          {
            "name": "go.yaml.in/yaml/v3",
            "direct": false,
            "version": "v3.0.3",
            "ecosystem": "go"
          },
          {
            "name": "golang.org/x/sys",
            "direct": false,
            "version": "v0.38.0",
            "ecosystem": "go"
          },
          {
            "name": "golang.org/x/text",
            "direct": false,
            "version": "v0.28.0",
            "ecosystem": "go"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 41,
        "direct_count": 14,
        "indirect_count": 27
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 7,
        "merged_prs": 2,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "posquit0",
          "commits": 156,
          "avatar_url": "https://avatars.githubusercontent.com/u/1484002?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "release.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        ".golangci.yml"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "go.sum"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 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": 0,
            "reason": "0 out of 2 merged PRs checked by a CI test -- score normalized to 0",
            "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": 10,
            "reason": "project has 9 contributing companies or organizations",
            "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": 10,
            "reason": "packaging workflow 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": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 8,
            "reason": "2 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "ddbf4aa5d77be8e462333b88b56d5e798cb4e9ae",
        "ran_at": "2026-07-28T11:53:49Z",
        "aggregate_score": 3.7,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-20T10:09:52Z",
      "oldest_open_prs": [
        {
          "number": 2,
          "created_at": "2026-07-05T11:58:13Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 3,
          "created_at": "2026-07-05T12:15:42Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 4,
          "created_at": "2026-07-05T12:16:09Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 5,
          "created_at": "2026-07-05T12:16:35Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 6,
          "created_at": "2026-07-05T12:21:43Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 7,
          "created_at": "2026-07-05T12:27:13Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 9,
          "created_at": "2026-07-05T12:38:11Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-07-05T13:49:38Z",
      "ci_last_conclusion": null,
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/tedilabs/ota",
    "host": "github.com",
    "name": "ota",
    "owner": "tedilabs"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 57,
      "inputs": {
        "security": 50,
        "vitality": 67,
        "community": 34,
        "governance": 59,
        "engineering": 68
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "moderate",
        "name": "Vitality",
        "value": 67,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 51,
            "inputs": {
              "commits_last_year": 156,
              "human_commit_share": 1,
              "days_since_last_push": 8,
              "active_weeks_last_year": 6
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 8 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "6/52 weeks with commits",
                "points": 4.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 6
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "156 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 156
                    }
                  }
                ],
                "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": 2,
              "latest_release_tag": "v0.1.2",
              "releases_from_tags": false,
              "days_since_latest_release": 8,
              "mean_days_between_releases": 0
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "2 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 8 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~0 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 0
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "at_risk",
        "name": "Community & Adoption",
        "value": 34,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 2,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "2 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "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": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (Apache-2.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "Apache-2.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "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": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 59,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 20,
            "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 9 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 72,
            "inputs": {
              "merged_prs": 2,
              "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": "2/2 decided PRs merged",
                "points": 38.2,
                "status": "met",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 2,
                      "decided": 2
                    }
                  }
                ],
                "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": "moderate",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 68,
            "inputs": {
              "followers": 113,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "tedilabs",
              "public_repos": 42,
              "account_age_days": 2061
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "113 followers of tedilabs",
                "points": 14.8,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 113,
                      "login": "tedilabs"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "42 public repos, account ~5 yr old",
                "points": 23.2,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 42
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 5
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 92,
            "inputs": {
              "packages": [
                "github.com/tedilabs/ota"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 8
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on go",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "go"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 8 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "2 published versions",
                "points": 12,
                "status": "partial",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "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": 68,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "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": ".golangci.yml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "0 out of 2 merged PRs checked by a CI test -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "topics": [
                "hacktoberfest",
                "lang-go",
                "okta",
                "sso",
                "tedilabs",
                "tui"
              ],
              "has_wiki": false,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "6 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 6
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 50,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": null,
            "notes": [],
            "value": 37,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 18,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 0,
              "scorecard_aggregate": 3.7
            },
            "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": "0 out of 2 merged PRs checked by a CI test -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 9 contributing companies or organizations",
                "points": 2.5,
                "status": "met",
                "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 detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "2 existing vulnerabilities detected",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "excellent",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): Indirect dependencies free of known advisories, No advisories left outstanding. Remaining weights renormalized. Matched 41 resolved dependencies against OSV. This repository publishes no package the index resolves, so the repository dependency graph was assessed instead. That graph mixes development and test pins with shipped dependencies, so only the declared runtime dependencies are scored; transitive findings are reported as context and excluded from the score. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "indirect_dependencies_free_of_known_advisories",
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_repository",
                "params": {
                  "assessed": 41
                }
              },
              {
                "code": "advisories_repo_graph_caveat",
                "params": {}
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 2,
              "affected_packages": 2,
              "assessed_packages": 41,
              "unassessed_packages": 0,
              "affected_by_severity": "unknown 2",
              "direct_affected_packages": 0
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "no direct dependency carries a known advisory",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "no_direct_advisories",
                    "params": {}
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "transitive set not separable from development and test dependencies in this scope",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_scope_not_separable",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory carries a publication date",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_no_publication_date",
                    "params": {}
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "excellent",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 41,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 9
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 66,
        "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.99,
              "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": "99 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 99,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": true,
              "typed_language": true,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "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": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".golangci.yml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Go (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "Dockerfile, lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Dockerfile, lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "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": 119335,
              "source_files_sampled": 278,
              "oversized_source_files": 2
            },
            "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": "2/278 source files over 60KB",
                "points": 54.6,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 278,
                      "oversized": 2
                    }
                  }
                ],
                "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"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-28T11:53:57.507946Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/t/tedilabs/ota.svg",
  "full_name": "tedilabs/ota",
  "license_state": "standard",
  "license_spdx": "Apache-2.0"
}

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.