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

canopen-python / canopen

CANopen for Python

PythonMIT★ 562 estrellas⑂ 359 forksdesde sept 2016Ver en GitHub ↗

canopen-python/canopen tiene un índice de salud de 69 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Engineering Quality (81/100) y la más baja, AI Readiness (51/100). Se actualizó por última vez hace 28 días. 2 personas concentran la mayor parte del trabajo reciente.

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

69
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

canopen-pythonOrganización
2 seguidores1 repositorio públicodesde feb 2025

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
PyPIcanopen2.4.1857.51942hace 355 días

Métricas por categoría

Vitalidad

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

63Moderado · 22% del índice global
Cómo se puntúa
28.8/36Recencia de push — último push hace 28 días
10.4/36Cadencia de commits — 15/52 semanas con commits
14.8/18Volumen de commits — 43 commits en el último año
10/10OpenSSF Scorecard: Maintained — 26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
Datos de entrada utilizados
commits_last_year43
human_commit_share1
days_since_last_push28
active_weeks_last_year15
Cómo se puntúa
27/27Publica versiones — 22 versiones publicadas
16.2/36Recencia de las versiones — última versión hace 355 días
12.6/27Cadencia de publicación — una versión cada ~237 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count22
latest_release_tagv2.4.1
releases_from_tagsno
days_since_latest_release355
mean_days_between_releases237
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

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

72Bueno · 18% del índice global
Cómo se puntúa
44.6/60Estrellas — 562 estrellas
21.3/25Forks — 359 forks
7.8/15Observadores — 26 observadores
Datos de entrada utilizados
forks359
stars562
watchers26
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (MIT)
0/18Guía CONTRIBUTING
0/13.5Código de conducta
0/7.2Plantilla de issues
0/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno
Cómo se puntúa
79.1/80Descargas mensuales — 857.519 descargas/mes en pypi
0/20Dependientes en el registro — no lo informa este ecosistema
Datos de entrada utilizados
packagescanopen
dependents
ecosystemspypi
total_downloads
monthly_downloads857.519
Excluidos de la puntuación (sin datos o no aplicable): Dependientes en el registro. Los pesos restantes se han renormalizado.

Sostenibilidad y Gobernanza

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

66Moderado · 24% del índice global
Cómo se puntúa
25.2/54Factor bus — la mitad de los commits recae en 2 contribuyente(s)
11.5/22.5Distribución de commits — el principal contribuyente firma el 49% de los commits
13.5/13.5Amplitud de contribuyentes — 57 contribuyentes
10/10OpenSSF Scorecard: Contributors — project has 11 contributing companies or organizations
Datos de entrada utilizados
bus_factor2
contributors_sampled57
top_contributor_share0,488
Cómo se puntúa
39.3/46.8Resolución de issues — 84% de issues cerradas
30.8/38.3Aceptación de PR — 230/286 PR decididos fusionados
10.5/15OpenSSF Scorecard: Code-Review — Found 19/25 approved changesets -- score normalized to 7
Datos de entrada utilizados
merged_prs230
open_issues53
closed_issues278
issue_closed_ratio0,84
closed_unmerged_prs56
Cómo se puntúa
30/30Respaldo de la propiedad — propiedad de una organización
0/20Dominio verificado
3.4/25Alcance del propietario — 2 seguidores de canopen-python
5/25Trayectoria — 1 repos públicos, cuenta de ~1 años
Datos de entrada utilizados
followers2
owner_typeOrganization
is_verified
owner_logincanopen-python
public_repos1
account_age_days517
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en pypi
26/35Recencia de publicación — última publicación hace 355 días
20/20Historial de versiones — 42 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagescanopen
ecosystemspypi
any_deprecatedno
min_days_since_publish355

Calidad de Ingeniería

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

81Bueno · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 3 flujo(s) de trabajo
24/24Pruebas presentes
0/16Configuración de linter
0/9.6Hooks de pre-commit
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 23 out of 23 merged PRs checked by a CI test -- score normalized to 10
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_configno
has_precommit_configno

Documentación

100Excelente
Cómo se puntúa
30/30README
25/25Directorio de documentación
15/15Sitio de documentación / página del proyecto — http://canopen.readthedocs.io/
10/10Descripción del repositorio
10/10Topics — 3 topics
10/10Wiki
Datos de entrada utilizados
topicscan, canopen, python
has_wiki
homepagehttp://canopen.readthedocs.io/
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?

64Moderado · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — sin datos
2.5/2.5CI-Tests — 23 out of 23 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
5.2/7.5Code-Review — Found 19/25 approved changesets -- score normalized to 7
2.5/2.5Contributors — project has 11 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
7.5/7.5Maintained — 26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
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
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6.8/7.5Vulnerabilities — 1 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate5,5
Excluidos de la puntuación (sin datos o no aplicable): branch_protection, signed_releases. Los pesos restantes se han renormalizado.
Cómo se puntúa
35/35Dependencias directas libres de avisos conocidos — ninguna dependencia directa tiene un aviso conocido
25/25Dependencias indirectas libres de avisos conocidos — ninguna dependencia indirecta tiene un aviso conocido
0/40Sin avisos pendientes — ningún aviso tiene fecha de publicación
Datos de entrada utilizados
sourceosv
advisories0
affected_packages0
assessed_packages4
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
Excluidos de la puntuación (sin datos o no aplicable): Sin avisos pendientes. Los pesos restantes se han renormalizado. Se cotejó el cierre de dependencias en tiempo de ejecución de pypi:canopen@2.4.1 —lo que arrastra la instalación del paquete publicado—: 4 paquetes. 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.

51Moderado · 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 — 91 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,91
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
18/18Arranque con un solo comando — doc/Makefile
22/22Pruebas automatizadas
0/11Configuración de lint / formato
11/11Verificación estática de tipos — canopen/py.typed
0/10Entorno reproducible
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
lockfiles
has_dockerfileno
typed_languageno
bootstrap_filesdoc/Makefile
has_devcontainerno
has_linter_configno
typecheck_configscanopen/py.typed
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
Cómo se puntúa
27/45Código verificable por tipos — Python con configuración de verificación de tipos (canopen/py.typed)
55/55Tamaños de archivo manejables — 0/49 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePython
largest_source_bytes40.222
source_files_sampled49
oversized_source_files0
Cómo se puntúa
0/40Esquema de API (OpenAPI/GraphQL/proto)
0/20Servidor MCP
40/40Ejemplos ejecutables — examples
Datos de entrada utilizados
example_dirsexamples
has_mcp_signalno
api_schema_files

Datos clave

562estrellas de GitHub
57contribuidores
43commits en los últimos 12 meses
28días desde el último push
22versiones publicadas
2factor bus
53issues abiertas
PyPIecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token

Más detalle

Historial de estrellas y forks 0 ★ / 359 ⇿
0Estrellas
359Forks
20Versiones

Cuándo se añadió cada estrella y fork, recopilado de GitHub y agrupado por día. El crecimiento acumulado se sitúa justo encima de las adiciones diarias que lo componen, de modo que ambos se leen en conjunto: la acumulación orgánica sostenida no se parece en nada a un pico abrupto y efímero. Cuando esa diferencia es medible, se informa como autenticidad del crecimiento.

0125250375350272016-102021-082026-06
Mayor 2Menor 13Parche 5

Cada punto abarca 9 días.

OpenSSF Scorecard 5.5 / 10
5.5agregado

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-26 21:17 UTC

10Binary-Artifactsno binaries found in the repo
n/dBranch-Protectioninternal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
10CI-Tests23 out of 23 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
7Code-ReviewFound 19/25 approved changesets -- score normalized to 7
10Contributorsproject has 11 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
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
0Security-Policysecurity policy file not detected
n/dSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
9Vulnerabilities1 existing vulnerabilities detected
Dependencias directas 1
RegistroPaqueteRestricción de versiónManifiesto
PyPIpython-can>= 3.0.0pyproject.toml
Todas las dependencias 10

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

RegistroPaqueteVersiónRelación
PyPIpython-candirecta
PyPIcanmatrixindirecta
PyPIfuroindirecta
PyPImypyindirecta
PyPIpytestindirecta
PyPIpytest-covindirecta
PyPIsetuptoolsindirecta
PyPIsetuptools-scmindirecta
PyPIsphinxindirecta
PyPIsphinx-autodoc-typehintsindirecta
Avisos de dependencias 0

Instalar pypi:canopen@2.4.1 arrastra 4 paquetes, directos y transitivos: 0 tienen avisos conocidos, de los cuales 0 son dependencias directas.

Ningún aviso conocido afecta a las dependencias evaluadas.

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": [
        "can",
        "canopen",
        "python"
      ],
      "is_fork": false,
      "size_kb": 854,
      "has_wiki": true,
      "homepage": "http://canopen.readthedocs.io/",
      "languages": {
        "Shell": 903,
        "Python": 393830
      },
      "pushed_at": "2026-06-28T16:01:52Z",
      "created_at": "2016-09-20T17:42:18Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-21T02:00:12Z",
      "description": "CANopen for Python",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "master",
      "license_spdx_raw": "MIT",
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": null,
      "name": null,
      "type": "Organization",
      "login": "canopen-python",
      "company": null,
      "location": null,
      "followers": 2,
      "avatar_url": "https://avatars.githubusercontent.com/u/200581454?v=4",
      "created_at": "2025-02-24T08:54:04Z",
      "is_verified": null,
      "public_repos": 1,
      "account_age_days": 517
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v2.4.1",
          "kind": "patch",
          "published_at": "2025-08-05T11:11:01Z"
        },
        {
          "tag": "v2.4.0",
          "kind": "minor",
          "published_at": "2025-08-05T10:03:28Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2024-06-13T20:35:27Z"
        },
        {
          "tag": "v2.2.0",
          "kind": "minor",
          "published_at": "2023-10-09T18:44:08Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2023-01-03T14:27:22Z"
        },
        {
          "tag": "v2.0.0",
          "kind": "major",
          "published_at": "2022-02-12T11:40:27Z"
        },
        {
          "tag": "v1.2.1",
          "kind": "patch",
          "published_at": "2021-02-20T09:02:35Z"
        },
        {
          "tag": "v1.2.0",
          "kind": "minor",
          "published_at": "2020-11-09T19:25:02Z"
        },
        {
          "tag": "v1.1.0",
          "kind": "minor",
          "published_at": "2020-03-05T19:45:23Z"
        },
        {
          "tag": "v1.0.0",
          "kind": "major",
          "published_at": "2019-10-03T18:33:06Z"
        },
        {
          "tag": "v0.9.0",
          "kind": "minor",
          "published_at": "2019-05-04T11:43:00Z"
        },
        {
          "tag": "v0.8.1",
          "kind": "patch",
          "published_at": "2019-03-06T12:53:29Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2018-11-21T21:06:14Z"
        },
        {
          "tag": "v0.7.1",
          "kind": "patch",
          "published_at": "2018-09-03T17:51:44Z"
        },
        {
          "tag": "v0.7.0",
          "kind": "minor",
          "published_at": "2018-08-05T18:32:13Z"
        },
        {
          "tag": "v0.6.0",
          "kind": "minor",
          "published_at": "2018-03-12T18:41:13Z"
        },
        {
          "tag": "v0.5.1",
          "kind": "patch",
          "published_at": "2017-10-19T18:07:03Z"
        },
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2017-09-27T17:40:21Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2017-01-13T11:48:46Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2016-11-13T19:13:48Z"
        },
        {
          "tag": "v0.3.0.dev5",
          "kind": "other",
          "published_at": "2016-09-26T06:52:37Z"
        },
        {
          "tag": "v0.3.0.dev4",
          "kind": "other",
          "published_at": "2016-09-26T06:53:55Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "14b3336bc973023e82325426adf6d71046ebcf55",
          "body": "- Fix bug where an exception in one EMCY callback would abort all\n  subsequent callbacks; now logs the exception and continues\n- Add test for callback exception handling (verifies fix)\n- Add tests for EmcyConsumer initialization, multiple callback order,\n  error reset variants, wait timeout edge cas\n[…]\nhelper function, including the emcy_received condition\nlock to avoid race conditions.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
          "is_bot": false,
          "headline": "emcy: Catch callback exceptions and expand test coverage (#659)",
          "author_name": "Frieder Schüler",
          "author_login": "friederschueler",
          "committed_at": "2026-06-28T15:28:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3b57fed8d3e5fd54b9fca2f6ebe344f346952cb3",
          "body": "When importing an EDS/DCF file, any key that is not part of the CiA 306\nstandard field set is now collected into a custom_options dict on the object.\nThis allows applications to round-trip vendor-specific metadata without\nlosing it.\n\n* Use getattr/setattr instead of __dict__ access for array entry attributes.\n\nCo-authored-by: FedericoSpada <FedericoSpada@users.noreply.github.com>\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "eds: Support custom_options for unknown EDS fields (#653)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-06-27T21:25:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2a4544aa4e960a373f35ffe5921bde931be2410e",
          "body": "…(#658)\n\n* eds: fix LowLimit/HighLimit parsing for all signed integer types\n\n- Reimplement _calc_bit_length() (only handled INTEGER8/16/32/64)\n  using the same structure types used in ODVariable.\n- Log a warning instead of silently ignoring invalid limit values\n- Add EDS test entries in sample.eds f\n[…]\n\n* Renamed _convert_variable → _decode_from_eds and _revert_variable → _encode_to_eds\n\nCo-authored-by: André Colomb <src@andre.colomb.de>\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
          "is_bot": false,
          "headline": "eds: Fix signed integer limit parsing for all bit widths, log errors …",
          "author_name": "Frieder Schüler",
          "author_login": "friederschueler",
          "committed_at": "2026-06-27T20:44:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ea0efa4db5ddf8266d7acf1b64ce41d7534e944a",
          "body": "The NMT state transition from INITIALISING to PRE-OPERATIONAL causes\nthe heartbeat to start automatically if the parameter is non-zero in\nobject 0x1017:00.  This check however raises an exception if the\nobject does not even exist.\n\nCover this state in tests and explicitly ignore a KeyError when asking\nfor the object.",
          "is_bot": false,
          "headline": "nmt: Fix NmtSlave on a node without a heartbeat producer time. (#672)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-06-24T08:35:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9aab2d430034ba4fbc74325890bdf29db8cd68ae",
          "body": "* Correct type handling in decode_bits() and encode_bits().\n\nContrary to the existing type hints, the methods can handle strings\nfor lookup as well as an arbitrary collection of integer bit offsets.\nCorrect the hints to reflect that and check at runtime to only lookup\nfrom strings.\n\nCorrect return t\n[…]\ncted types for indexing the Bits.\n\n* Test non-string keys for bit access.\n\n* Fix slice-based access without step, useful exception without stop.\n\n* Fix slice-based access without start, cover in test.",
          "is_bot": false,
          "headline": "Correct type handling for bitwise variable access (#663)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-22T12:27:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6688d6399279609f2e4b4cee2b5034f0291b32b6",
          "body": "Use a simple integer zero as dummy value for \"unset\" in instances.\nThis fixes some type checker errors.  Add type hints for functions\ninvolving those values.\n\nFix test coverage for unset and unknown data type.",
          "is_bot": false,
          "headline": "Clarify ODVariable.data_type cannot be None (#666)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-22T11:19:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fc5621bf0bb4f2031c835bc60ec10579499a5b19",
          "body": "* variable: Value description in log message only for integers.\n\n* Raise TypeError when requesting value description of non-int data.\n\nAccording to the type hints, the mapping mechanism was only ever\nintended for integers (emulating an enumeration).  Avoids a later\nDictionaryError for the missing ke\n[…]\nStyle fix (black).\n\nBetter overview if not on one line.\n\n* Avoid else after raise.\n\nSimplify by using the walrus operator.\n\n* Extend test coverage for ODVariable.\n\n* Extend test coverage for Variable.",
          "is_bot": false,
          "headline": "variable: Fix type-related issues with desc property (#667)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-22T09:33:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1d6bd8d1f55593e16cc98dc3662c0e6f9a9df39a",
          "body": "* Fix too narrow type annotations in emcy and network.\n* Return None explicitly where return type expects it.\n* Annotate ODVariable and ODArray parent attribute as for ODRecord.\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
          "is_bot": false,
          "headline": "Fix some wrong type hints and mypy errors (#664)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-12T23:06:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "62cf726b59d3dd22810921f367170897a64f7216",
          "body": "- State402 enum and state decoding from statusword\n- Command word generation for state transitions\n- Next-state calculation through the state machine\n- Operation mode switching and reading\n- TPDO callback handling for statusword updates\n- Lookup table consistency checks\n- Full state machine transitions simulating drive controlword /\n  statusword exchange\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Add unit tests for CiA 402 profile (#644)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-05-12T18:43:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7f8347f40e6794b3b3240436df3672733d691a79",
          "body": "* network: Avoid notifier re-creating in connect function\n\n* test_network: Add regression tests for notifier lifecycle\n\nAdd tests to verify that:\n- connect() does not recreate an existing notifier\n- disconnect() releases the notifier\n- disconnect() releases the notifier even when check() raises\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
          "is_bot": false,
          "headline": "network: Avoid notifier re-creating in connect function (#621)",
          "author_name": "xndcn",
          "author_login": "xndcn",
          "committed_at": "2026-05-08T12:16:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a4e61d7715b571c186cbe3f2a5d9de5ec964f88a",
          "body": "* eds: Centralize object type logic in build_variable.\n\nPass object type to build_variable() instead of the boolean is_domain\nparameter.  Add type hints to aid in validating all call sites have\nbeen adjusted.\n\n* Remove unnecessary duplicate lookup from ConfigParser.\n\n* Fix mypy errors in build_varia\n[…]\nsumed\nonly None type) on the OD variable classes, to accept strings from\neds.\n\nSilence mypy in import_eds() for a custom attribute addition.\n\n* Respect line-length in build_variable + small style fix.",
          "is_bot": false,
          "headline": "eds: Style and type fixes around build_variable() (#661)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-07T12:34:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "929f4d40e2a85b3498bcc850ccc8eb86a547e19b",
          "body": "Co-authored-by: Christian Sandberg <christiansandberg@users.noreply.github.com>\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "doc: Should check unconfigured state before running fastscan (#290)",
          "author_name": "Youngmin Kim",
          "author_login": "ymkim92",
          "committed_at": "2026-05-07T10:39:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9dbb9fd909ac64fd2075cfb1b09412ae9420ce27",
          "body": "The BaseNode.id attribute can be initialized either from the given\nnode_id parameter, or from the Object Dictionary if zero was passed.\nThe None value as mentioned in the docstring actually violates the\ntype hint, so drop that suggestion.  Make sure the inferred type of\nthe self.id attribute does no\n[…]\n docstring for LocalNode as well, to document the expected\nnode_id \"invalid\" value.  And because there was none at all before,\ninclude a bit of functional description to set the expectations\nstraight.",
          "is_bot": false,
          "headline": "Make sure any BaseNode always has a valid Node ID (#657)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-07T09:36:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "35f4b7fbfdf3623931459203784ee0634026ac14",
          "body": "Line-length 96 is a compomise because 80 is problematic with existing\ncode, but we want a bit of headroom for the next common limit of 100.\n\nString \"normalization\" is complete noise in most cases, that could be\nenabled again after some general cleanup.\n\nMake sure that ruff agrees with the previously used import ordering\nstyle from isort.",
          "is_bot": false,
          "headline": "Add some basic rules for ruff linter and black formatting (#660)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-07T07:54:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5dd8e6f723932e4dcdb02a8d32412525cbfc3ad4",
          "body": "Fix encode_phys precision loss for int64 with factor=1",
          "is_bot": false,
          "headline": "Merge pull request #648 from bizfsc/fix/encode-phys-int64-precision",
          "author_name": "Frieder Schüler",
          "author_login": "friederschueler",
          "committed_at": "2026-05-06T20:08:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b73daa6f9efb0e9799ab48baba2869a16e122d66",
          "body": "Memorized DOMAIN ObjectType for a proper export",
          "is_bot": false,
          "headline": "Merge pull request #655 from FedericoSpada/export_domain_objects",
          "author_name": "Frieder Schüler",
          "author_login": "friederschueler",
          "committed_at": "2026-05-06T20:04:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aaa495e70f23206aafcadd38b73643de6dbafff8",
          "body": null,
          "is_bot": false,
          "headline": "Small changes requested by the maintainers",
          "author_name": "Federico Spada",
          "author_login": "FedericoSpada",
          "committed_at": "2026-05-06T19:50:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1eba930eb9ba9b49d4aa5bfc8bf6b5b699a9aaf1",
          "body": null,
          "is_bot": false,
          "headline": "Add test for float 1.0 factor promoting to float type.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-06T09:26:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "43ae8926e48528ab3b44a66bb339d7a717514dd0",
          "body": null,
          "is_bot": false,
          "headline": "Reword docstrings to state facts, not changes.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-06T09:21:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c05b49d0cd76830df4a474645d7570db976340f1",
          "body": "* Add unit tests for Variable class.\n* Raise ValueError on invalid fmt argument.\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
          "is_bot": false,
          "headline": "Add unit tests for Variable class (#656)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-05-05T20:29:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "473fbd7356a4cbf5f773d3c418413c1f6d76a66b",
          "body": "Change method signatures to accept 'object' as required by the\nLiskov substitution principle (Mapping/Container/object supertypes).\n\nFor __eq__, return NotImplemented for incompatible types instead of\nraising AttributeError. For __contains__ in SdoArray, add isinstance\ncheck to handle non-int arguments gracefully.",
          "is_bot": false,
          "headline": "Fix __contains__ and __eq__ override signatures (#649)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-05-05T19:32:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "67bed3c3ad1e31dd292fd3ebb25130adaf255e5c",
          "body": "- Replace List[int] with list[int] (PEP 585, supported since Python 3.9)\n- Add type annotations for untyped variables (var-annotated)\n- Ignore typing on import for 'canmatrix':\nThis should have the advantage of affecting various type checkers, not\nonly mypy.  When the upstream canmatrix package does support type\nhints (work is underway), this place is likely to get noticed and the\ncomment removed.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Fix trivial mypy errors (#647)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-05-05T19:19:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f1c01e45ce4227aa9360096ad7fa9708d7802a7e",
          "body": "Changed to save whether a Variable has ObjectType==DOMAIN while importing an EDS, so that it is possible to export it unchanged.",
          "is_bot": false,
          "headline": "Implemented the management for DOMAIN Objects",
          "author_name": "Federico Spada",
          "author_login": "FedericoSpada",
          "committed_at": "2026-05-04T20:44:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2133067bf03691cefc6b9cc41a90218424edd70c",
          "body": "Skip the float division in encode_phys when factor is 1 (the default).\nPreviously, `value /= self.factor` always performed float division,\ncausing up to 10 bits of precision loss for large int64 values\n(e.g. 0x55554444AAAABBBB became 0x55554444AAAABC00).\n\nUse `!= 1` for the comparison since we are c\n[…]\nthe original bug)\n- int type preservation with factor=1\n- rounding with integer factor != 1 (existing behavior preserved)\n- float factor (existing behavior preserved)\n\nBased on the discussion in #611.",
          "is_bot": false,
          "headline": "Fix encode_phys precision loss for int64 with factor=1",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-04-29T11:59:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4e789fe6627b2bd5e4f0133bdff883ea84e60200",
          "body": "- Switch state global/selective commands\n- Node ID and bit timing configuration\n- Activate bit timing and store configuration\n- LSS address inquiry (vendor/product/revision/serial)\n- Timeout and error handling\n- Fast scan protocol\n- Obsolete method aliases\n- LssError exception\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Add unit tests for LSS master (#643)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-04-29T08:26:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "80c6da32e5d79a636a18475d1bfe49d23c0cb827",
          "body": "Also use comparison instead of range() for a minor performance\nimprovement.",
          "is_bot": false,
          "headline": "eds: Fix typo in manufacturer_idices → manufacturer_indices (#642)",
          "author_name": "Frieder Schüler",
          "author_login": "bizfsc",
          "committed_at": "2026-04-29T06:04:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2be54f3e2827de6488ef2c642aac35cfe068ff93",
          "body": null,
          "is_bot": false,
          "headline": "Mention ValueError cause in transmit() docstring.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-04-23T21:09:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "baba4a94482fbd71c26c7a9ee4afd2aa2b4c2607",
          "body": null,
          "is_bot": false,
          "headline": "Mention ValueError cause in docstring.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-04-23T21:05:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fe494bba8f2365179308e839192f9fb28ad04ef2",
          "body": null,
          "is_bot": false,
          "headline": "pdo: Handle None cob_id correctly in __repr__ (#640)",
          "author_name": "Yash Chaudhary",
          "author_login": "stoicfeats",
          "committed_at": "2026-04-23T21:05:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cb1622a931f391fcf6bf3f68718fc48be8efcfc8",
          "body": "The fallback behavior in PdoMaps.__getitem__() tried looking up by\nparameter record index even when no com_offset or map_offset was\ndefined. This caused a possibly wrong PdoMap object to be\nreturned. For example with 0x15FF, the lookup for 0x1600 would\nsucceed.\n\nAdd a test to make sure that an nonexistant parameter record index for\n0x15FF actually does raise a KeyError. Skip the fallback lookup by\nparameter index if no record offsets are defined.",
          "is_bot": false,
          "headline": "pdo: Fix wrongfully returned object for missing index (#636)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-04-07T11:00:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9685880e19f348c0d282ff33296b6902104ef96c",
          "body": "The extended access patterns by communication parameter record index\nonly work for TPDOs. The lower bound check on the number range\nexcludes anything below 0x1600, thus RPDO communication parameters do\nnot work.\n\nExtend test to cover access via 0x1400 and 0x1600 yielding the same\nobject.",
          "is_bot": false,
          "headline": "pdo: Fix lower bound check in access by index (#628)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2026-03-12T19:07:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db9110e9cd0545b2aa7426243f169d0f863a7640",
          "body": "Move the ARR, DOMAIN, RECORD, VAR constant definitions to their own\n\"objectcodes\" module and add missing ones from the standard.  Rename\nARR to ARRAY.  Add a reverse lookup function code2str() for\nconvenience.\n\nAdjust all uses within the eds module.",
          "is_bot": false,
          "headline": "eds: Factor out object code constants.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-10-26T20:41:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7c6dee5a1b040a3de8ea18263f7d8dad9120e076",
          "body": "Import the OD* classes directly, as the repeatedly used\nobjectdictionary.OD* naming is redundant.",
          "is_bot": false,
          "headline": "eds: Refactor OD classes imports.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-10-26T20:22:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e58a65318eee4b0e3befb1a9f244be7173a0960d",
          "body": "Fix mixed up mapping parameter record index numbers in class PDO. The\naccess by this index number mixed up the two number ranges.  The CiA\n301 standard defines:\n\n* Object 1600h to 17FFh: RPDO mapping parameter\n* Object 1A00h to 1BFFh: TPDO mapping parameter\n\nThe test was also wrong, because it added\n[…]\nwell,\ninstead of overwriting it with a custom dictionary.  To allow this\ndummy usage, adjust its constructor to skip generating entries.  Add\nsome explanation why relative indices cannot be used here.",
          "is_bot": false,
          "headline": "Fix and extend PDO selection by record IDs (fixes #607) (#613)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-09-22T19:39:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "593f0621f41ce3f94c09322ca8414c7e649b128e",
          "body": "* Raise KeyError for zero index.\n* Properly test different access type return values.\n\nAugment test_pdo_getitem() to not only check the mapped object values.\nWhat's more important is the type of object returned, and whether it\nis the correct object (identical to other access method results).",
          "is_bot": false,
          "headline": "PdoBase: Forbid getting item with zero index (fixes #581) (#609)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-09-22T19:18:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "54ac5c2d5ca0324c528f2b5356be06be2b3def1f",
          "body": "Reduce the amount of code within the critical section holding the\nlock.  Unpack and log first.  Update timestamp and state while locked\nand notify condition waiters.  Invoke callbacks after releasing the\nlock.",
          "is_bot": false,
          "headline": "NmtMaster: Delay heartbeat callbacks after state update (#601)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-09-08T21:13:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "829f4628634f1a6f169edfe14254b6aa54a398f5",
          "body": "…) (#600)\n\nRaise a RuntimeError when a SYNC transmission task has previously been\nstarted.  Make sure to clear the internal _task attribute when\nstopping, to make the check work.",
          "is_bot": false,
          "headline": "SyncProducer: Prevent from starting another periodic task (fixes #582…",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-09-08T21:07:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be9c56d4fe696bb3d2cd63834b3a1245773976b0",
          "body": "* Disregard OD variable description in SdoClient.upload().\n\nThe upload method should behave as a raw producer of byte data.\nInterpretation of the returned data based on the Object Dictionary is\nthe responsibility of the SdoVariable class, which delegates to the\ngeneric ODVariable decoding functions.\n[…]\nonly incompatible change.\n\n* Test expedited upload with 8 byte padded frame.\n\nThe existing test response is actually wrong, since the standard\nmandates four bytes \"data\" regardless of the other flags.",
          "is_bot": false,
          "headline": "Disregard OD variable description in SdoClient.upload() (#592)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-08-18T20:51:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1582ba5b0d5f8f7365d86432ceaca5137b4f4670",
          "body": "* Update license metadata.\n\nAvoid deprecation warnings with modern setuptools, which will stop\nsupporting a table as the project.license option in pyproject.toml\nsoon.  Require a modern version >= 77 to make use of the alternative\noptions.\n\n* Modernize Dict and List type annotations.\n\nSince Python 3.9 we can use the built-in types directly.\n\n* Simplify type import.\n\n* Modernize Iterator type annotations.\n\nSince Python 3.9 the collections.abc module can be used directly for\ntyping.",
          "is_bot": false,
          "headline": "build: Bump minimum Python version to 3.9 (fixes #585) (#610)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-08-18T20:42:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "317fd3d61513b33ddb1aaf8a3ea2c23384a275ee",
          "body": "Commit 1fc63b5f4835eba80462c7892a2cd00f786ec9ce tried to limit the\nbuilt packages to ignore other arbitrary top-level directories.  But\nin turn, it disabled the auto-discovery in setuptools completely.\nThis results in build warnings for the missing sub-packages that would\nhave been auto-discovered.\n\nSwitch to an explicit setuptools.packages.find configuration which\nstill limits to the canopen top-level package.",
          "is_bot": false,
          "headline": "build: Fix package auto-discovery (ref #468) (#608)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-08-05T11:06:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b50616b57afc9ef048469b2064b1b8e9bc0434c3",
          "body": "Partially reverts commit 62e9c1f851cb8721355d22c8571f2b41599c1b36.\nThese RST roles are not supported in the basic renderer used on PyPI.\nThus the package is rejected for upload.",
          "is_bot": false,
          "headline": "Remove Sphinx roles from README.rst",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-08-05T10:47:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "49905727b26fd273eecdb534946e67021d4666ff",
          "body": "* Define constants for all standardized SDO abort codes.\n* Type annotations for constants (Final).\n* Use constants in SDO client and server.\n* Keep literals in tests, but comment and format.",
          "is_bot": false,
          "headline": "sdo: Define constants for all standardized abort codes (#596)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-08-05T09:38:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d35ce74e197bbc88e5a3b796ef5963154421e968",
          "body": "…(#606)",
          "is_bot": false,
          "headline": "eds: Avoid float formatting in BaudRate_xxx export keys (fixes #605) …",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-07-27T20:47:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0130eb88b0c137bdd41cdaa25463d020ae3df6c9",
          "body": "SDO abort messages were sent in some cases when a response times out\nor has an unexpected server command specifier. But not consistently,\nthus the following cases are now added:\n\n    Mismatched scs:\n        ReadableStream, after upload segment request\n        WritableStream, after download initiate \n[…]\ne).\n\n* Avoid duplicate SDO abort messages on timeout in _block_ack()\n   and _end_upload().\n\n* Add docstring to read_response() and detail exceptions.\n\nCo-authored-by: samsam <samfire.pilote@gmail.com>",
          "is_bot": false,
          "headline": "sdo.client: Add missing abort messages (#594)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-07-13T12:40:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "09f6e9e8e9497108790328aed9839f2552acdbae",
          "body": "* Skip saving and log read-only PDO parameters.\n\nFor PDO communication or mapping parameters which are read-only on a\nnode, the PdoMap.save() method currently aborts with an SDO exception.\nHowever, individual parameters may simply not support configuration\nchanges.  Rely on the Object Dictionary to \n[…]\nries, log\nwhat is not being saved.\n\n* Switch to a LocalNode in TestPDO to not require a network.\n\n* Test PdoMap.save() skipping readonly entries.\n\nCo-authored-by: Erlend E. Aasland <erlend@python.org>",
          "is_bot": false,
          "headline": "Gracefully handle readonly entries in PdoMap.save() (fixes #541) (#593)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-06-22T20:17:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8d9aa6c67c07bfe39baae1d02b058e644de23bdc",
          "body": "Co-authored-by: André Colomb <github.com@andre.colomb.de>",
          "is_bot": false,
          "headline": "Prevent warnings if SDO setup fails (#590)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2025-06-18T15:14:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "80349e1326787fe4793858e0477c798489352029",
          "body": "A data size of zero bytes cannot be encoded in the \"number of bytes\nthat do not contain data\" semantics, as that would require n=4, which\nis however limited to two bits (maximum value 3).  So the SDO\nexpedited upload protocol simply cannot convey that condition.  The\ncurrent implementation however s\n[…]\notocol violation by responding with an SDO abort code of\n0800 0024h, No data available.\n\nAdd a test case for this condition, verifying that the client raises\nthe appropriate SdoAbortedError exception.",
          "is_bot": false,
          "headline": "Abort SDO server upload when the object has zero length (#587)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-06-17T16:37:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "de0e644b7d2c6ce11294be40c1da4ab33a15206b",
          "body": "If the lookup by name returns an object (not None), the boolean\nexpression still checks whether that object is equivalent to boolean\nTrue.  This needlessly calls the object's __len__() method, while what\nwe're interested in is just whether an object was found.",
          "is_bot": false,
          "headline": "Fix OD item lookup evaluating subobject's length (fixes #588) (#589)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2025-06-15T18:53:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7fb914a12e239afbb8acc4134e9eb5a6afdbcb9d",
          "body": "When an OD entry is found in SdoClient.upload(), the truncation is\ncurrently skipped only for those types explicitly listed as variable\nlength data (domain, strings).  That causes unknown types to be\ntruncated to whatever their length indicates, which is usually one\nbyte.\n\nInvert the condition to ch\n[…]\nknown size,\na.k.a. those listed in STRUCT_TYPES where the required length can be\ndeduced from the structure format.\n\nRe-enable the unit tests which were skipped based on the previously\nbuggy behavior.",
          "is_bot": false,
          "headline": "Avoid truncating unknown types in SDO upload (fixes #436) (#591)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-06-15T18:20:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7ddb19b2ca7b7a85b9cef3090249b00293dd4b4a",
          "body": "We are waiting for the sequence number to be the same as the last good\nknown sequence number, to start considering the messages.  However,\nthis is wrong because the SDO server will start sending the discarded\nsegments at the start of a new block, which means the sequence number\nrestarts from 1.\n\nRes\n[…]\nssion.\n\nAdd a block retransmit test. This test passes with this implementation\nbut will fail with an invalid CRC without fix for discarded segments.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Fix check for retransmission of discarded block segments (#546)",
          "author_name": "Samuel Lee",
          "author_login": "samsamfire",
          "committed_at": "2025-06-11T07:55:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c0a4ae50b0487d5d683e6eeff1fd3c3069747a02",
          "body": "Co-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Correctly apply offset to custom timestamp value (fixes #563) (#576)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2025-06-11T07:20:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "09c1ddd4094358081dd4a79803dacf744ab72ecf",
          "body": "Co-authored-by: Erlend E. Aasland <erlend@python.org>",
          "is_bot": false,
          "headline": "Add some type annotations for the nmt module (#586)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-06-09T20:09:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bc917f057500cfa66cbe9b24fa80e268a40f7907",
          "body": "Co-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Type annotate everywhere Network is used (#575)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2025-06-09T12:01:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cf9f33ea88cbade2ee08037926c5a653396e6fab",
          "body": "Estalish a minimal mypy config that ensures annotations are compatible\nwith Python 3.8 (the oldest supported Python version).\n\nAdd a forgiving CI job that does not affect the status of the GitHub\nworkflow run.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Add mypy CI and config (ref #358) (#518)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2025-06-09T11:50:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e840449fbf9ddf810d14899279cb72e61a2447f0",
          "body": "* Fix multiple assosciations and removals of networks\n* Simplify cleanup logic in Network.unsubscribe().\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Fix multiple associations and removals of networks (#573)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2025-04-28T20:36:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f1a71da20b918760ace20c62297f41132cd6618c",
          "body": null,
          "is_bot": false,
          "headline": "Bump Codecov action to version 5.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2025-04-27T18:37:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "48cca7c655f8c932b1f761a8edc11d0702397fab",
          "body": null,
          "is_bot": false,
          "headline": "Update GitHub URLs to new organization",
          "author_name": "Christian Sandberg",
          "author_login": "christiansandberg",
          "committed_at": "2025-04-06T10:25:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5382f245d8ede61b113c6a648cb99b199667f0cd",
          "body": null,
          "is_bot": false,
          "headline": "Updated debian packaging for reprepro (#557)",
          "author_name": "Tony Baltovski",
          "author_login": "tonybaltovski",
          "committed_at": "2025-02-20T20:20:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ae71853be7fb42870bb5763066b0c3c50d015669",
          "body": "Previously, when attempting a write of \"\" to a VISIBLE_STRING, the\r\nexpedited SDO request would not be sent and the transaction would\r\ntimeout.  To resolve this, if the transaction size is known to be 0, a\r\nsegmented transfer is used.",
          "is_bot": false,
          "headline": "Fix SDO writes of empty strings (#551)",
          "author_name": "Thomas Willson",
          "author_login": "willson556",
          "committed_at": "2025-01-05T22:01:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ffbd10f44da89e407622c82b8587fcc64e3f2f49",
          "body": "Follow recommendations from flake8 regarding import order and\ngrouping.  Put two blank lines after the top import block in each\nfile.\n\nImport CanError directly from can package in canopen.sdo.client\ninstead of from canopen.network, where it was not even used.",
          "is_bot": false,
          "headline": "Clean up imports ordering.",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-10-18T07:20:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c781a225f60b1b5d39cfdad713d3d5bb4799c844",
          "body": "… (#525)\n\nAdd an _UnitializedNetwork class and a singleton\r\n_UNINITIALIZED_NETWORK instance.  It can replace the dummy \"None\"\r\nvalue for attribute initializations, which can then be properly typed\r\nas Network to avoid static type checking errors.\r\n\r\nThis has the benefit of not needing `self.network \n[…]\nge (accessing a network without associating it\r\nfirst), which a static checker cannot detect reliably.  The dummy\r\nclass provides a descriptive exception message when any attribute is\r\naccessed on it.",
          "is_bot": false,
          "headline": "Add a dummy object to designate \"uninitialized\" networks (fixes #511)…",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-10-17T19:36:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6bc90a817f049b52fbeb1201c699e90ca36a6136",
          "body": "The count entry itself is not part of the data (according to CiA 301),\r\nthus should not be yielded from an iterator.  That matches the\r\nbehavior of SdoArray, which also yields only the array contents.\r\n\r\nNote that the basis of returned record sub-objects is still the subset\r\ndescribed by the OD, whi\n[…]\nicated by the record's\r\nsubindex 0.  Thus the count (and iteration set) is reduced by one\r\nelement only if the subindex 0 was actually part of this subset.\r\n\r\n* No need to expect test failure anymore.",
          "is_bot": false,
          "headline": "Skip the \"highest subindex\" entry when iterating over SdoRecord (#538)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-08-17T21:05:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "273bab17400f0eb14c5e9e7714fa88de3fce2e7f",
          "body": "* Remove one of the sub-objects in a record for testing.\r\n\r\nAdjust the EDS test accordingly, since the record length only counts\r\nsub-objects that have an actual description.\r\n\r\n* Flesh out the Pre-defined error field object in sample.eds.\r\n\r\nSwitch from CompactSubObj to actual sub-entries.  Leave o\n[…]\n SdoArray dynamically generated member variables.\r\n\r\n* Add test for SdoArray length and iteration count.\r\n\r\n* Add test for SdoRecord length and iteration count.\r\n\r\n* Expect failure on last added test.",
          "is_bot": false,
          "headline": "Tests: Verify some basic SDO record and array assumptions (#539)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-08-17T20:59:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "210374a857b8afbb9fb2bf2a93d374f4e0f70563",
          "body": "* Speed up NMT tests\r\n\r\n* Make test_emcy, test_eds, and test_od executable scripts\r\n\r\n* Harden and speed up test_network_send_periodic()\r\n\r\n* Shorten heartbeat period in test_local\r\n\r\n* Join as cleanup action\r\n\r\n* Fix test_network_send_periodic() periodicity test",
          "is_bot": false,
          "headline": "Tests: speed up test suite (#537)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-15T06:46:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1b7c06f57ba8a46afbddfa2b4ca29196e63a465d",
          "body": "* Annotate PeriodicMessageTask in NmtMaster and NmtSlave.\r\n\r\n* Type annotate Network.notifier.\r\n\r\n* Remove duplicate initialization of PeriodicMessageTask._task.\r\n\r\nThe attribute is set within the method directly following.  So there\r\nis no need to initialize it to None before, only to confuse type\r\ncheckers.",
          "is_bot": false,
          "headline": "Fix some type checker errors in network module (#534)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-08-14T21:17:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6ef4f5e35d84a7d776d1758842bc8138215e9715",
          "body": "* Add configurable timeouts for Network.notifier.\r\n\r\nMove the hard-coded 1 second maximum cycle duration passed during\r\ncan.Notifier construction to a class variable NOTIFIER_CYCLE.\r\nExplicitly specify the timeout to stop the notifier in another class\r\nvariable NOTIFIER_SHUTDOWN_TIMEOUT, matching th\n[…]\nuring tests.\r\n\r\nExplicitly set the NOTIFIER_SHUTDOWN_TIMEOUT to zero in all Network\r\ninstances created for unit tests.\r\n\r\nThis reduces total test running time from ~30 to ~9 seconds when run\r\nlocally.",
          "is_bot": false,
          "headline": "Make Network.notifier timeouts customizable (fixes #530) (#536)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-08-14T10:47:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "932c6212bf077596dbfec91fd6ca66308142b972",
          "body": "Also:\r\n- bump GitHub checkout action to v4\r\n- bump GitHub setup-python action to v5\r\n- ignore README and LICENSE files\r\n- invoke pip according to best practise\r\n\r\nFollow-up of #498",
          "is_bot": false,
          "headline": "CI: Add requirements-dev.txt and add pip cache (#533)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-12T10:59:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4301c4bfed020565bfdb939deaf30b8d49de5727",
          "body": "* Tests: harden test_emcy_consumer_wait\r\n\r\nWait a little bit longer before dispatching the EMCY packet, and start\r\nthe one-shot timer just before invoking the wait() call.\r\n\r\n* Better resource management",
          "is_bot": false,
          "headline": "Tests: harden test_emcy_consumer_wait (#532)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-12T10:01:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "83594ac89ee8f07d3bf346f3ceeab3b52d954625",
          "body": null,
          "is_bot": false,
          "headline": "Tests: improve SYNC coverage (#531)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-12T08:22:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "78f6dded0b714e542dd0c5bac7af73b8012b499c",
          "body": null,
          "is_bot": false,
          "headline": "Docs: remove unused Sphinx config 'html_static_path' (#521)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-12T07:45:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5004363e011b5f22b5e16825328189c81889a205",
          "body": null,
          "is_bot": false,
          "headline": "Tests: improve EMCY coverage (#529)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-12T07:27:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "28ee18c14a628671ebeb6105f3cd1ed02af02cc4",
          "body": "The .active functionality was purged on June 9, 2017\r\nwith commit a28ef2762.",
          "is_bot": false,
          "headline": "Remove unused NodeScanner.active member (#527)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-11T20:50:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d75c5f09b11b1ffee200bd8813efac5c8f0127e8",
          "body": "This fixes up the tests added with #517 (commit\r\nd1c28e51b93c7d45fce40187672e7ab7a0616cac).",
          "is_bot": false,
          "headline": "Make node scanner tests more deterministic (#526)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-11T20:40:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d1c28e51b93c7d45fce40187672e7ab7a0616cac",
          "body": null,
          "is_bot": false,
          "headline": "Tests: improve coverage of NodeScanner (#517)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-06T20:08:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ecf216a760c8f0016826bb0c4f8e1f7944951d21",
          "body": "- take into account that payloads with DATA1 may be in flight when we\r\n  update the task\r\n- test task.stop()\r\n- use more lenient timeout\r\n- use threading.Condition for improved readability\r\n- wait for periodicity to be established before validation; some\r\n  platforms are a little bit flaky\r\n\r\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Tests: harden TestNetwork and TestNmt even more (#508)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-06T20:03:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7d7e2f17c6aeef6aea2e6970f3f80064a3b8b2cc",
          "body": null,
          "is_bot": false,
          "headline": "Tests: improve coverage of Network (#515)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-08-06T19:43:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a196e1e3b62bddcfd62cea9fc5293efe8a8cc0a4",
          "body": "Always require dependencies to be installed.\r\n\r\nResolved #509",
          "is_bot": false,
          "headline": "Purge import hack from network.py (#510)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-10T14:47:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55d870e2d4fd1f36141712ebad318e6b611ea50c",
          "body": "Windows fails with a permission error if the temporary file is already\r\nopened.\r\n\r\nFixes #501",
          "is_bot": false,
          "headline": "Tests: make sure temporary files are pre-closed (#507)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-10T14:44:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3aa509db9fea8094516e5eb717be3df182f0751a",
          "body": "- make tests deterministic\r\n- use cleanup hooks\r\n- check the periodicity of heartbeats, by number of triggers\r\n- roughly check the interval",
          "is_bot": false,
          "headline": "Tests: harden TestNetwork (#505)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-09T11:06:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5207b322907b9d3fa2a4343ed17dc4d89fe26109",
          "body": "Fixes #500",
          "is_bot": false,
          "headline": "Ensure NmtBase.state always returns a string (#506)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-09T10:58:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fa74236b3f3312c03d09e72de3fa6d3cf077fbec",
          "body": null,
          "is_bot": false,
          "headline": "Add tests for NMT base and NMT master (#504)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-09T10:15:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c4560daa37bec67a663046834a42b8058b5ceedc",
          "body": null,
          "is_bot": false,
          "headline": "Build docs in CI (fixes #497) (#498)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-09T06:54:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6bd39ba8f1fef7e32732fcc61ae548aa2392b7e0",
          "body": "The canmatrix optional dependency was removed on Oct 10, 2021 with commit\r\nc46228f. It is now added back as an optional dependency, using the same\r\nname as previously: db_export.\r\n\r\nTo install the dependency:\r\n\r\n$ python3 -m pip install 'canopen[db_export]'\r\n\r\nResolves #488",
          "is_bot": false,
          "headline": "Add back PdoBase.export() dependency (canmatrix) (#493)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-09T06:51:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "08eba81f519e637e0c20d58560ca9d6637e0adfe",
          "body": "Give NMT tests their own file, and establish a util.py for the test\r\nsuite for common stuff like the location of sample EDS files.\r\n\r\nConsistently use SAMPLE_EDS instead of EDS_PATH.",
          "is_bot": false,
          "headline": "Tests: establish test/util.py and test/test_nmt.py (#503)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-08T13:42:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c413eaffd0e13389512cfdf43dd51694842a427b",
          "body": null,
          "is_bot": false,
          "headline": "Git: correctly ignore docs build directory (#499)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-08T10:41:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "62e9c1f851cb8721355d22c8571f2b41599c1b36",
          "body": "- add formatting to Git branch names\r\n- add formatting to Python module names\r\n- spell out Python version support",
          "is_bot": false,
          "headline": "README: Add formatting and amend doc build instructions (#494)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-06T17:44:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "36c8488febc20e1d351135c90dd8966ab1d07dfb",
          "body": "Handle PDO-related objects through the associated methods in the PdoBase class when initializing a RemoteNode from an ObjectDictionary with provided values (DCF file usually).  This avoids the locally cached mapping information to get out of sync with the PDO configuration parameters, and thus elimi\n[…]\n.read() method must honor DCF-provided values for this purpose.  It previously only respected the OD default values when called with from_od=True.\r\n\r\nCo-authored-by: André Colomb <src@andre.colomb.de>",
          "is_bot": false,
          "headline": "Add support for loading DCF configuration to remote node (#427)",
          "author_name": "meifonglow",
          "author_login": "meifonglow",
          "committed_at": "2024-07-04T20:45:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "02ebc0c44fac791ca44f232c6d6ac1950617bfab",
          "body": null,
          "is_bot": false,
          "headline": "Add tests for bitrate parsing in import_eds() (#495)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-04T10:40:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f1315d3df4354cf02ac2acc70b53b91761c2f242",
          "body": "The CiA306 spec is not quite clear on which fields are mandatory\r\nwithin the DeviceComissioning section.  Thus it should be assumed that\r\nthe two options handled here could be missing.  Instead of raising an\r\nexception, ignore the absence of the Baudrate setting.",
          "is_bot": false,
          "headline": "Gracefully handle missing Baudrate setting in DCF (#492)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-07-03T22:28:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6a4ca1184e219c64768fbb0eb8434b04320d4803",
          "body": "Only for DCF-files (which include a DeviceComissioning section), the Node-ID from the file is stored in the ObjectDictionary instance.  When given an override value via the `import_od(..., node_id=...)` argument, that was however not applied to the OD attribute, but only used for $NODEID interpolati\n[…]\nn.\r\n\r\nMake sure the interpolated values match the stored node_id in this case.\r\n\r\nAlso handle a missing NodeID option within the DeviceComissioning section gracefully, instead of raising an exception.",
          "is_bot": false,
          "headline": "Save the passed node ID to the resulting OD in import_od() (#484)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-03T22:08:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2938a908fb3cb93a65db5d30a7d889a08f66c956",
          "body": "The bustype keyword has long been replaced by \"interface\" and the\r\nformer is deprecated in python-can.  The Network class passes it\r\nthrough without caring for the name, but documentation and tests\r\nreferred to the older name regularly.",
          "is_bot": false,
          "headline": "Adapt to renamed \"bustype\" parameter on can.Bus (#489)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-07-03T21:20:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cc37aee4e172a5a83f9f9dc956cc66d82bd1a8b7",
          "body": "Fixes #485",
          "is_bot": false,
          "headline": "Bail early if an unsupported doctype is passed to export_od() (#486)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-03T09:40:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "92a6e11168f5072403a8ec03a7de0195854674cb",
          "body": null,
          "is_bot": false,
          "headline": "Remove temporary files created in EDS tests (#482)",
          "author_name": "Svein Seldal",
          "author_login": "sveinse",
          "committed_at": "2024-07-02T15:25:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "65bfaab0fd4d826be125dc0045834b179e47fb22",
          "body": null,
          "is_bot": false,
          "headline": "Reduce diff",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T11:56:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "71da1bc38f56e602d95686adec22abd3ee0d3939",
          "body": "Clean-up post #474",
          "is_bot": false,
          "headline": "Remove temporary files created in EDS tests",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T11:54:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e3af0eb31bdfde2203d56f1dd953dbd23b4b6609",
          "body": "* Test export_eds() to stdout\r\n* Use StringIO as a context manager iso. addCleanup\r\n* Expand the test to cover most paths\r\n* Expand test for unknown doctypes\r\n* Use subTest for better debugging",
          "is_bot": false,
          "headline": "Increase export_eds() test coverage (#474)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T11:26:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "028a57f49510803eeef4965b95f3aa76ce18e648",
          "body": "Resolves #472\r\n\r\n* Expose and improve the docstrings of export_od and import_id\r\n* export_od(od, None, doctype) does not return a string; it writes to stdout\r\n* Clarify 'doc_type' details\r\n* Add exception info",
          "is_bot": false,
          "headline": "Expose and improve the docstrings of `export_od` and `import_id` (#473)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T11:21:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d141e13970ce5bb5e696a97b92131356d34092bc",
          "body": "Spell out how it works and link to PdoBase.read()\r\n\r\nMoreover, add return annotation to make sure that's included in the\r\nrendered docs.",
          "is_bot": false,
          "headline": "Improve the RemoteNode.load_configuration() docs (#477)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T11:19:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "503918c9bd9ff0499a604c7805e7627ca38fb332",
          "body": "Fixes #475\r\n\r\n* Test the offending suffix is part of the error message\r\n* Make the exception message more helpful",
          "is_bot": false,
          "headline": "Raise ValueError for unknown formats in the import/export OD APIs (#476)",
          "author_name": "Erlend E. Aasland",
          "author_login": "erlend-aasland",
          "committed_at": "2024-07-02T10:32:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5734f37ee758e1d025701b4d40e0b89a57db2317",
          "body": "* Reintroduce PdoMap and PdoVariable to public API of sub-package pdo.\r\n\r\nWith commit 0285f582beb344de708ce8308274a99bc04988a1, the Map class\r\nwas inadvertently removed from the imports directly available within\r\nthe canopen.pdo package.  However, it is referenced as\r\ncanopen.pdo.PdoMap in the p402 \n[…]\nc API.\r\n\r\n* Use type hints for PdoMap in BaseNode402.\r\n\r\nReplace the sphinx parameter type documentation with a proper type\r\nhint annotation.  Same for the tpdo_pointers and rpdo_pointers\r\nattributes.",
          "is_bot": false,
          "headline": "Export PdoMap and PdoVariable classes in pdo module (#464)",
          "author_name": "André Colomb",
          "author_login": "acolomb",
          "committed_at": "2024-07-02T10:04:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 22,
      "commits_last_year": 43,
      "latest_release_at": "2025-08-05T11:11:01Z",
      "latest_release_tag": "v2.4.1",
      "releases_from_tags": false,
      "days_since_last_push": 28,
      "active_weeks_last_year": 15,
      "days_since_latest_release": 355,
      "mean_days_between_releases": 237
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 37,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "canopen",
          "exists": true,
          "license": null,
          "keywords": [
            "Development Status :: 5 - Production/Stable",
            "Intended Audience :: Developers",
            "License :: OSI Approved :: MIT License",
            "Operating System :: OS Independent",
            "Programming Language :: Python :: 3 :: Only",
            "Topic :: Scientific/Engineering"
          ],
          "ecosystem": "pypi",
          "matches_repo": true,
          "registry_url": "https://pypi.org/project/canopen/",
          "is_deprecated": false,
          "latest_version": "2.4.1",
          "repository_url": "https://github.com/canopen-python/canopen",
          "versions_count": 42,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 857519,
          "first_published_at": "2016-09-20T18:01:29.141719Z",
          "latest_published_at": "2025-08-05T11:11:39.476697Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 355
        }
      ]
    },
    "popularity": {
      "forks": 359,
      "stars": 562,
      "watchers": 26,
      "fork_history": {
        "days": [
          {
            "date": "2016-10-22",
            "count": 1
          },
          {
            "date": "2016-11-01",
            "count": 1
          },
          {
            "date": "2016-11-03",
            "count": 1
          },
          {
            "date": "2016-11-21",
            "count": 1
          },
          {
            "date": "2016-12-13",
            "count": 1
          },
          {
            "date": "2016-12-22",
            "count": 1
          },
          {
            "date": "2017-01-09",
            "count": 1
          },
          {
            "date": "2017-04-13",
            "count": 1
          },
          {
            "date": "2017-05-08",
            "count": 1
          },
          {
            "date": "2017-07-06",
            "count": 1
          },
          {
            "date": "2017-07-27",
            "count": 1
          },
          {
            "date": "2017-08-09",
            "count": 1
          },
          {
            "date": "2017-09-01",
            "count": 2
          },
          {
            "date": "2017-09-15",
            "count": 1
          },
          {
            "date": "2017-10-09",
            "count": 1
          },
          {
            "date": "2017-11-04",
            "count": 1
          },
          {
            "date": "2017-11-06",
            "count": 1
          },
          {
            "date": "2018-01-03",
            "count": 1
          },
          {
            "date": "2018-01-09",
            "count": 1
          },
          {
            "date": "2018-01-11",
            "count": 1
          },
          {
            "date": "2018-02-01",
            "count": 1
          },
          {
            "date": "2018-03-22",
            "count": 1
          },
          {
            "date": "2018-03-30",
            "count": 1
          },
          {
            "date": "2018-04-03",
            "count": 1
          },
          {
            "date": "2018-04-18",
            "count": 1
          },
          {
            "date": "2018-06-19",
            "count": 1
          },
          {
            "date": "2018-07-17",
            "count": 1
          },
          {
            "date": "2018-07-24",
            "count": 1
          },
          {
            "date": "2018-07-26",
            "count": 1
          },
          {
            "date": "2018-08-07",
            "count": 2
          },
          {
            "date": "2018-08-17",
            "count": 1
          },
          {
            "date": "2018-09-05",
            "count": 1
          },
          {
            "date": "2018-10-26",
            "count": 1
          },
          {
            "date": "2018-10-28",
            "count": 1
          },
          {
            "date": "2018-11-06",
            "count": 1
          },
          {
            "date": "2018-11-27",
            "count": 1
          },
          {
            "date": "2018-12-26",
            "count": 1
          },
          {
            "date": "2019-01-08",
            "count": 1
          },
          {
            "date": "2019-01-14",
            "count": 1
          },
          {
            "date": "2019-02-16",
            "count": 1
          },
          {
            "date": "2019-03-07",
            "count": 1
          },
          {
            "date": "2019-03-14",
            "count": 2
          },
          {
            "date": "2019-03-22",
            "count": 1
          },
          {
            "date": "2019-03-28",
            "count": 1
          },
          {
            "date": "2019-04-02",
            "count": 1
          },
          {
            "date": "2019-05-17",
            "count": 1
          },
          {
            "date": "2019-05-21",
            "count": 1
          },
          {
            "date": "2019-06-06",
            "count": 1
          },
          {
            "date": "2019-06-12",
            "count": 1
          },
          {
            "date": "2019-06-13",
            "count": 1
          },
          {
            "date": "2019-06-23",
            "count": 1
          },
          {
            "date": "2019-07-24",
            "count": 1
          },
          {
            "date": "2019-08-23",
            "count": 1
          },
          {
            "date": "2019-08-24",
            "count": 1
          },
          {
            "date": "2019-08-27",
            "count": 1
          },
          {
            "date": "2019-09-04",
            "count": 1
          },
          {
            "date": "2019-09-09",
            "count": 1
          },
          {
            "date": "2019-09-14",
            "count": 1
          },
          {
            "date": "2019-10-20",
            "count": 1
          },
          {
            "date": "2019-10-21",
            "count": 1
          },
          {
            "date": "2019-11-08",
            "count": 1
          },
          {
            "date": "2019-11-14",
            "count": 2
          },
          {
            "date": "2019-11-19",
            "count": 1
          },
          {
            "date": "2020-01-20",
            "count": 1
          },
          {
            "date": "2020-01-28",
            "count": 1
          },
          {
            "date": "2020-01-30",
            "count": 1
          },
          {
            "date": "2020-03-19",
            "count": 1
          },
          {
            "date": "2020-04-07",
            "count": 1
          },
          {
            "date": "2020-04-15",
            "count": 1
          },
          {
            "date": "2020-04-24",
            "count": 1
          },
          {
            "date": "2020-04-29",
            "count": 1
          },
          {
            "date": "2020-05-13",
            "count": 1
          },
          {
            "date": "2020-05-18",
            "count": 1
          },
          {
            "date": "2020-06-02",
            "count": 1
          },
          {
            "date": "2020-08-11",
            "count": 1
          },
          {
            "date": "2020-09-22",
            "count": 1
          },
          {
            "date": "2020-09-30",
            "count": 1
          },
          {
            "date": "2020-10-11",
            "count": 1
          },
          {
            "date": "2020-10-31",
            "count": 1
          },
          {
            "date": "2020-11-02",
            "count": 1
          },
          {
            "date": "2020-11-20",
            "count": 1
          },
          {
            "date": "2021-01-13",
            "count": 1
          },
          {
            "date": "2021-01-17",
            "count": 1
          },
          {
            "date": "2021-01-20",
            "count": 1
          },
          {
            "date": "2021-02-08",
            "count": 1
          },
          {
            "date": "2021-02-17",
            "count": 1
          },
          {
            "date": "2021-02-18",
            "count": 1
          },
          {
            "date": "2021-02-24",
            "count": 1
          },
          {
            "date": "2021-03-03",
            "count": 1
          },
          {
            "date": "2021-03-09",
            "count": 1
          },
          {
            "date": "2021-03-22",
            "count": 1
          },
          {
            "date": "2021-04-05",
            "count": 1
          },
          {
            "date": "2021-04-07",
            "count": 1
          },
          {
            "date": "2021-04-14",
            "count": 1
          },
          {
            "date": "2021-04-29",
            "count": 1
          },
          {
            "date": "2021-05-02",
            "count": 1
          },
          {
            "date": "2021-06-09",
            "count": 1
          },
          {
            "date": "2021-07-27",
            "count": 1
          },
          {
            "date": "2021-08-05",
            "count": 1
          },
          {
            "date": "2021-09-02",
            "count": 1
          },
          {
            "date": "2021-09-06",
            "count": 1
          },
          {
            "date": "2021-10-11",
            "count": 1
          },
          {
            "date": "2021-10-23",
            "count": 1
          },
          {
            "date": "2021-10-28",
            "count": 1
          },
          {
            "date": "2021-11-03",
            "count": 1
          },
          {
            "date": "2021-11-09",
            "count": 1
          },
          {
            "date": "2021-11-11",
            "count": 1
          },
          {
            "date": "2021-11-12",
            "count": 1
          },
          {
            "date": "2021-11-27",
            "count": 1
          },
          {
            "date": "2021-12-09",
            "count": 1
          },
          {
            "date": "2021-12-10",
            "count": 1
          },
          {
            "date": "2021-12-31",
            "count": 1
          },
          {
            "date": "2022-01-06",
            "count": 1
          },
          {
            "date": "2022-02-11",
            "count": 2
          },
          {
            "date": "2022-03-08",
            "count": 2
          },
          {
            "date": "2022-03-15",
            "count": 1
          },
          {
            "date": "2022-03-22",
            "count": 1
          },
          {
            "date": "2022-04-03",
            "count": 1
          },
          {
            "date": "2022-05-04",
            "count": 1
          },
          {
            "date": "2022-05-07",
            "count": 1
          },
          {
            "date": "2022-05-11",
            "count": 1
          },
          {
            "date": "2022-05-12",
            "count": 1
          },
          {
            "date": "2022-05-13",
            "count": 1
          },
          {
            "date": "2022-05-17",
            "count": 1
          },
          {
            "date": "2022-05-20",
            "count": 1
          },
          {
            "date": "2022-06-14",
            "count": 1
          },
          {
            "date": "2022-07-08",
            "count": 1
          },
          {
            "date": "2022-08-06",
            "count": 1
          },
          {
            "date": "2022-08-25",
            "count": 1
          },
          {
            "date": "2022-08-26",
            "count": 1
          },
          {
            "date": "2022-08-30",
            "count": 1
          },
          {
            "date": "2022-09-01",
            "count": 1
          },
          {
            "date": "2022-09-14",
            "count": 1
          },
          {
            "date": "2022-09-22",
            "count": 1
          },
          {
            "date": "2022-11-03",
            "count": 1
          },
          {
            "date": "2023-01-23",
            "count": 1
          },
          {
            "date": "2023-01-29",
            "count": 1
          },
          {
            "date": "2023-01-30",
            "count": 1
          },
          {
            "date": "2023-02-02",
            "count": 1
          },
          {
            "date": "2023-02-09",
            "count": 1
          },
          {
            "date": "2023-02-22",
            "count": 1
          },
          {
            "date": "2023-02-27",
            "count": 1
          },
          {
            "date": "2023-03-06",
            "count": 1
          },
          {
            "date": "2023-03-14",
            "count": 1
          },
          {
            "date": "2023-03-16",
            "count": 1
          },
          {
            "date": "2023-04-28",
            "count": 1
          },
          {
            "date": "2023-05-09",
            "count": 1
          },
          {
            "date": "2023-05-18",
            "count": 1
          },
          {
            "date": "2023-05-24",
            "count": 1
          },
          {
            "date": "2023-05-26",
            "count": 1
          },
          {
            "date": "2023-06-06",
            "count": 1
          },
          {
            "date": "2023-06-19",
            "count": 1
          },
          {
            "date": "2023-07-06",
            "count": 1
          },
          {
            "date": "2023-07-07",
            "count": 1
          },
          {
            "date": "2023-07-19",
            "count": 1
          },
          {
            "date": "2023-08-24",
            "count": 2
          },
          {
            "date": "2023-09-01",
            "count": 1
          },
          {
            "date": "2023-10-18",
            "count": 1
          },
          {
            "date": "2023-10-24",
            "count": 1
          },
          {
            "date": "2023-10-28",
            "count": 1
          },
          {
            "date": "2023-11-14",
            "count": 1
          },
          {
            "date": "2023-11-28",
            "count": 1
          },
          {
            "date": "2023-11-29",
            "count": 1
          },
          {
            "date": "2023-12-02",
            "count": 1
          },
          {
            "date": "2023-12-19",
            "count": 1
          },
          {
            "date": "2024-01-08",
            "count": 1
          },
          {
            "date": "2024-02-28",
            "count": 1
          },
          {
            "date": "2024-03-10",
            "count": 1
          },
          {
            "date": "2024-03-20",
            "count": 1
          },
          {
            "date": "2024-03-28",
            "count": 1
          },
          {
            "date": "2024-04-24",
            "count": 1
          },
          {
            "date": "2024-05-20",
            "count": 1
          },
          {
            "date": "2024-06-04",
            "count": 1
          },
          {
            "date": "2024-06-29",
            "count": 1
          },
          {
            "date": "2024-07-10",
            "count": 1
          },
          {
            "date": "2024-07-15",
            "count": 2
          },
          {
            "date": "2024-07-29",
            "count": 1
          },
          {
            "date": "2024-10-05",
            "count": 1
          },
          {
            "date": "2024-10-28",
            "count": 1
          },
          {
            "date": "2024-11-25",
            "count": 1
          },
          {
            "date": "2025-02-12",
            "count": 1
          },
          {
            "date": "2025-02-25",
            "count": 1
          },
          {
            "date": "2025-03-02",
            "count": 1
          },
          {
            "date": "2025-04-04",
            "count": 1
          },
          {
            "date": "2025-04-23",
            "count": 1
          },
          {
            "date": "2025-06-17",
            "count": 1
          },
          {
            "date": "2025-07-21",
            "count": 2
          },
          {
            "date": "2025-08-05",
            "count": 1
          },
          {
            "date": "2025-08-06",
            "count": 1
          },
          {
            "date": "2025-08-20",
            "count": 1
          },
          {
            "date": "2025-09-01",
            "count": 1
          },
          {
            "date": "2025-09-02",
            "count": 1
          },
          {
            "date": "2025-09-10",
            "count": 1
          },
          {
            "date": "2025-09-14",
            "count": 1
          },
          {
            "date": "2025-09-21",
            "count": 1
          },
          {
            "date": "2025-09-27",
            "count": 1
          },
          {
            "date": "2025-11-07",
            "count": 1
          },
          {
            "date": "2025-12-01",
            "count": 1
          },
          {
            "date": "2025-12-09",
            "count": 1
          },
          {
            "date": "2026-01-05",
            "count": 3
          },
          {
            "date": "2026-01-30",
            "count": 1
          },
          {
            "date": "2026-02-27",
            "count": 2
          },
          {
            "date": "2026-03-05",
            "count": 2
          },
          {
            "date": "2026-03-06",
            "count": 2
          },
          {
            "date": "2026-03-07",
            "count": 5
          },
          {
            "date": "2026-03-08",
            "count": 3
          },
          {
            "date": "2026-03-09",
            "count": 6
          },
          {
            "date": "2026-03-10",
            "count": 5
          },
          {
            "date": "2026-03-12",
            "count": 3
          },
          {
            "date": "2026-03-13",
            "count": 2
          },
          {
            "date": "2026-03-14",
            "count": 1
          },
          {
            "date": "2026-03-15",
            "count": 3
          },
          {
            "date": "2026-03-16",
            "count": 1
          },
          {
            "date": "2026-03-17",
            "count": 2
          },
          {
            "date": "2026-03-18",
            "count": 5
          },
          {
            "date": "2026-03-19",
            "count": 4
          },
          {
            "date": "2026-03-20",
            "count": 2
          },
          {
            "date": "2026-03-21",
            "count": 7
          },
          {
            "date": "2026-03-22",
            "count": 2
          },
          {
            "date": "2026-03-23",
            "count": 2
          },
          {
            "date": "2026-03-24",
            "count": 4
          },
          {
            "date": "2026-03-25",
            "count": 1
          },
          {
            "date": "2026-03-26",
            "count": 6
          },
          {
            "date": "2026-03-27",
            "count": 3
          },
          {
            "date": "2026-03-28",
            "count": 1
          },
          {
            "date": "2026-03-29",
            "count": 2
          },
          {
            "date": "2026-03-30",
            "count": 1
          },
          {
            "date": "2026-03-31",
            "count": 2
          },
          {
            "date": "2026-04-01",
            "count": 3
          },
          {
            "date": "2026-04-02",
            "count": 4
          },
          {
            "date": "2026-04-03",
            "count": 2
          },
          {
            "date": "2026-04-04",
            "count": 1
          },
          {
            "date": "2026-04-06",
            "count": 2
          },
          {
            "date": "2026-04-07",
            "count": 5
          },
          {
            "date": "2026-04-08",
            "count": 1
          },
          {
            "date": "2026-04-09",
            "count": 1
          },
          {
            "date": "2026-04-10",
            "count": 2
          },
          {
            "date": "2026-04-11",
            "count": 1
          },
          {
            "date": "2026-04-12",
            "count": 1
          },
          {
            "date": "2026-04-13",
            "count": 1
          },
          {
            "date": "2026-04-14",
            "count": 2
          },
          {
            "date": "2026-04-15",
            "count": 2
          },
          {
            "date": "2026-04-16",
            "count": 3
          },
          {
            "date": "2026-04-17",
            "count": 3
          },
          {
            "date": "2026-04-18",
            "count": 2
          },
          {
            "date": "2026-04-20",
            "count": 1
          },
          {
            "date": "2026-04-21",
            "count": 3
          },
          {
            "date": "2026-04-22",
            "count": 1
          },
          {
            "date": "2026-04-23",
            "count": 2
          },
          {
            "date": "2026-04-24",
            "count": 1
          },
          {
            "date": "2026-04-27",
            "count": 1
          },
          {
            "date": "2026-04-28",
            "count": 1
          },
          {
            "date": "2026-04-29",
            "count": 2
          },
          {
            "date": "2026-04-30",
            "count": 3
          },
          {
            "date": "2026-05-06",
            "count": 2
          },
          {
            "date": "2026-05-07",
            "count": 1
          },
          {
            "date": "2026-05-11",
            "count": 1
          },
          {
            "date": "2026-05-12",
            "count": 1
          },
          {
            "date": "2026-05-20",
            "count": 1
          },
          {
            "date": "2026-05-21",
            "count": 1
          },
          {
            "date": "2026-06-23",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 350,
        "total_forks": 359
      },
      "star_history": null,
      "open_issues_and_prs": 73
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "examples"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "doc/Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "canopen/py.typed"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 40222,
      "source_files_sampled": 49,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "doc/requirements.txt",
        "pyproject.toml",
        "requirements-dev.txt",
        "setup.py"
      ],
      "advisories": {
        "error": null,
        "scope": "published_package",
        "source": "osv",
        "findings": [],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 4,
        "malicious_count": 0,
        "assessed_package": "pypi:canopen@2.4.1",
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "python-can",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">= 3.0.0"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "python-can",
            "direct": true,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "canmatrix",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "furo",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "mypy",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "pytest",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "pytest-cov",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "setuptools",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "setuptools-scm",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "sphinx",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          },
          {
            "name": "sphinx-autodoc-typehints",
            "direct": false,
            "version": null,
            "ecosystem": "pypi"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 10,
        "direct_count": 1,
        "indirect_count": 9
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 20,
        "merged_prs": 230,
        "open_issues": 53,
        "closed_ratio": 0.84,
        "closed_issues": 278,
        "closed_unmerged_prs": 56
      },
      "bus_factor": 2,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "christiansandberg",
          "commits": 317,
          "avatar_url": "https://avatars.githubusercontent.com/u/3647996?v=4"
        },
        {
          "type": "User",
          "login": "acolomb",
          "commits": 113,
          "avatar_url": "https://avatars.githubusercontent.com/u/6996887?v=4"
        },
        {
          "type": "User",
          "login": "af-silva",
          "commits": 38,
          "avatar_url": "https://avatars.githubusercontent.com/u/11874980?v=4"
        },
        {
          "type": "User",
          "login": "erlend-aasland",
          "commits": 31,
          "avatar_url": "https://avatars.githubusercontent.com/u/13780613?v=4"
        },
        {
          "type": "User",
          "login": "fredrikhoyer",
          "commits": 29,
          "avatar_url": "https://avatars.githubusercontent.com/u/35274532?v=4"
        },
        {
          "type": "User",
          "login": "sveinse",
          "commits": 25,
          "avatar_url": "https://avatars.githubusercontent.com/u/13770135?v=4"
        },
        {
          "type": "User",
          "login": "friederschueler",
          "commits": 8,
          "avatar_url": "https://avatars.githubusercontent.com/u/4674229?v=4"
        },
        {
          "type": "User",
          "login": "bizfsc",
          "commits": 7,
          "avatar_url": "https://avatars.githubusercontent.com/u/202658520?v=4"
        },
        {
          "type": "User",
          "login": "tuwuhs",
          "commits": 5,
          "avatar_url": "https://avatars.githubusercontent.com/u/2083418?v=4"
        },
        {
          "type": "User",
          "login": "luminize",
          "commits": 5,
          "avatar_url": "https://avatars.githubusercontent.com/u/2206842?v=4"
        }
      ],
      "contributors_sampled": 57,
      "top_contributor_share": 0.488
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "pr-linters.yaml",
        "pythonpackage.yml",
        "pythonpublish.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "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": null,
            "reason": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 10,
            "reason": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 7,
            "reason": "Found 19/25 approved changesets -- score normalized to 7",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 10,
            "reason": "project has 11 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": 10,
            "reason": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 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": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "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": 9,
            "reason": "1 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "14b3336bc973023e82325426adf6d71046ebcf55",
        "ran_at": "2026-07-26T21:17:15Z",
        "aggregate_score": 5.5,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-06-28T15:28:58Z",
      "oldest_open_prs": [
        {
          "number": 267,
          "created_at": "2021-09-06T23:22:48Z",
          "last_comment_at": "2024-06-04T21:02:54Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 298,
          "created_at": "2022-03-08T07:29:23Z",
          "last_comment_at": "2026-05-04T09:33:30Z",
          "last_comment_author": "bizfsc"
        },
        {
          "number": 343,
          "created_at": "2023-01-15T21:18:27Z",
          "last_comment_at": "2023-03-15T21:25:47Z",
          "last_comment_author": "samsamfire"
        },
        {
          "number": 352,
          "created_at": "2023-03-15T21:22:02Z",
          "last_comment_at": "2026-05-06T08:44:45Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 359,
          "created_at": "2023-03-27T13:58:16Z",
          "last_comment_at": "2026-06-10T21:43:55Z",
          "last_comment_author": "sveinse"
        },
        {
          "number": 386,
          "created_at": "2023-07-07T21:08:14Z",
          "last_comment_at": "2023-08-02T13:00:22Z",
          "last_comment_author": "Obsnold"
        },
        {
          "number": 419,
          "created_at": "2024-04-05T13:09:41Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 454,
          "created_at": "2024-06-04T13:30:49Z",
          "last_comment_at": "2026-04-16T21:28:01Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 524,
          "created_at": "2024-07-29T20:47:57Z",
          "last_comment_at": "2024-08-12T19:53:05Z",
          "last_comment_author": "codecov-commenter"
        },
        {
          "number": 547,
          "created_at": "2024-11-07T20:55:02Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 559,
          "created_at": "2025-02-18T08:06:00Z",
          "last_comment_at": "2026-05-22T11:59:41Z",
          "last_comment_author": "codecov"
        },
        {
          "number": 603,
          "created_at": "2025-07-21T09:29:33Z",
          "last_comment_at": "2025-07-21T09:41:08Z",
          "last_comment_author": "codecov"
        },
        {
          "number": 614,
          "created_at": "2025-09-11T16:58:48Z",
          "last_comment_at": "2026-03-24T21:02:15Z",
          "last_comment_author": "zaporozhets"
        },
        {
          "number": 651,
          "created_at": "2026-04-29T19:01:25Z",
          "last_comment_at": "2026-05-12T07:43:02Z",
          "last_comment_author": "bizfsc"
        },
        {
          "number": 654,
          "created_at": "2026-05-04T08:44:40Z",
          "last_comment_at": "2026-06-27T21:38:16Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 668,
          "created_at": "2026-05-26T08:32:51Z",
          "last_comment_at": "2026-05-26T08:34:28Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 669,
          "created_at": "2026-05-26T14:48:30Z",
          "last_comment_at": "2026-06-28T21:24:55Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 673,
          "created_at": "2026-06-04T21:09:21Z",
          "last_comment_at": "2026-06-04T21:10:10Z",
          "last_comment_author": "codecov"
        },
        {
          "number": 674,
          "created_at": "2026-06-24T14:27:21Z",
          "last_comment_at": "2026-06-28T16:04:51Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 675,
          "created_at": "2026-06-27T22:05:43Z",
          "last_comment_at": "2026-06-27T22:13:59Z",
          "last_comment_author": "acolomb"
        }
      ],
      "last_merged_pr_at": "2026-06-28T15:28:19Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 47,
          "created_at": "2017-10-29T12:33:07Z",
          "last_comment_at": "2021-10-12T02:30:58Z",
          "last_comment_author": "sveinse"
        },
        {
          "number": 143,
          "created_at": "2019-02-25T19:05:14Z",
          "last_comment_at": "2020-06-04T14:19:44Z",
          "last_comment_author": "arne123"
        },
        {
          "number": 145,
          "created_at": "2019-03-11T12:17:17Z",
          "last_comment_at": "2019-03-14T09:10:11Z",
          "last_comment_author": "grw1983"
        },
        {
          "number": 179,
          "created_at": "2020-03-05T09:54:02Z",
          "last_comment_at": "2025-01-25T02:27:38Z",
          "last_comment_author": "ZeroErrControl"
        },
        {
          "number": 203,
          "created_at": "2020-10-13T12:14:05Z",
          "last_comment_at": "2020-10-20T07:15:45Z",
          "last_comment_author": "illuusio"
        },
        {
          "number": 261,
          "created_at": "2021-08-12T12:16:12Z",
          "last_comment_at": "2024-06-14T08:01:47Z",
          "last_comment_author": "friederschueler"
        },
        {
          "number": 272,
          "created_at": "2021-10-12T02:29:21Z",
          "last_comment_at": "2026-06-05T21:03:07Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 286,
          "created_at": "2021-11-29T13:51:47Z",
          "last_comment_at": "2022-02-18T20:03:56Z",
          "last_comment_author": "af-silva"
        },
        {
          "number": 321,
          "created_at": "2022-06-29T14:51:48Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 325,
          "created_at": "2022-08-12T06:36:59Z",
          "last_comment_at": "2022-08-15T05:13:54Z",
          "last_comment_author": "gardenvariety42"
        },
        {
          "number": 329,
          "created_at": "2022-09-27T11:58:17Z",
          "last_comment_at": "2022-09-29T08:48:37Z",
          "last_comment_author": "TudorMorosanu"
        },
        {
          "number": 358,
          "created_at": "2023-03-26T22:54:44Z",
          "last_comment_at": "2025-06-22T20:08:15Z",
          "last_comment_author": "acolomb"
        },
        {
          "number": 372,
          "created_at": "2023-04-12T16:32:52Z",
          "last_comment_at": "2023-04-14T17:36:28Z",
          "last_comment_author": "superlogan72"
        },
        {
          "number": 374,
          "created_at": "2023-04-21T09:52:42Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 377,
          "created_at": "2023-05-12T09:51:33Z",
          "last_comment_at": "2024-06-13T08:26:20Z",
          "last_comment_author": "friederschueler"
        },
        {
          "number": 383,
          "created_at": "2023-06-23T14:14:31Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 385,
          "created_at": "2023-07-07T20:56:52Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 391,
          "created_at": "2023-08-19T03:57:31Z",
          "last_comment_at": "2024-06-13T09:30:42Z",
          "last_comment_author": "friederschueler"
        },
        {
          "number": 392,
          "created_at": "2023-08-28T16:07:51Z",
          "last_comment_at": "2023-09-13T14:13:48Z",
          "last_comment_author": "sam-tj"
        },
        {
          "number": 396,
          "created_at": "2023-09-05T10:53:25Z",
          "last_comment_at": "2024-01-07T12:30:59Z",
          "last_comment_author": "acolomb"
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/canopen-python/canopen",
    "host": "github.com",
    "name": "canopen",
    "owner": "canopen-python"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 69,
      "inputs": {
        "security": 64,
        "vitality": 63,
        "community": 72,
        "governance": 66,
        "engineering": 81
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "moderate",
        "name": "Vitality",
        "value": 63,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "commits_last_year": 43,
              "human_commit_share": 1,
              "days_since_last_push": 28,
              "active_weeks_last_year": 15
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 28 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 28
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "15/52 weeks with commits",
                "points": 10.4,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 15
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "43 commits in the last year",
                "points": 14.8,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 43
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "moderate",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 62,
            "inputs": {
              "releases_count": 22,
              "latest_release_tag": "v2.4.1",
              "releases_from_tags": false,
              "days_since_latest_release": 355,
              "mean_days_between_releases": 237
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "22 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 22
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 355 days ago",
                "points": 16.2,
                "status": "partial",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 355
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~237 days",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 237
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 28,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 28 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 28
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "good",
        "name": "Community & Adoption",
        "value": 72,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "good",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 74,
            "inputs": {
              "forks": 359,
              "stars": 562,
              "watchers": 26,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "562 stars",
                "points": 44.6,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 562
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "359 forks",
                "points": 21.3,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 359
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "26 watchers",
                "points": 7.8,
                "status": "partial",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 26
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "excellent",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 99,
            "inputs": {
              "packages": [
                "canopen"
              ],
              "dependents": null,
              "ecosystems": "pypi",
              "total_downloads": null,
              "monthly_downloads": 857519
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "857,519 downloads/month across pypi",
                "points": 79.1,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 857519,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 66,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "moderate",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 60,
            "inputs": {
              "bus_factor": 2,
              "contributors_sampled": 57,
              "top_contributor_share": 0.488
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "2 contributor(s) cover half of all commits",
                "points": 25.2,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 49% of commits",
                "points": 11.5,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 49
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "57 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 57
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 11 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 81,
            "inputs": {
              "merged_prs": 230,
              "open_issues": 53,
              "closed_issues": 278,
              "issue_closed_ratio": 0.84,
              "closed_unmerged_prs": 56
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "84% of issues closed",
                "points": 39.3,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 84
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "230/286 decided PRs merged",
                "points": 30.8,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 230,
                      "decided": 286
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 19/25 approved changesets -- score normalized to 7",
                "points": 10.5,
                "status": "partial",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 38,
            "inputs": {
              "followers": 2,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "canopen-python",
              "public_repos": 1,
              "account_age_days": 517
            },
            "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": "2 followers of canopen-python",
                "points": 3.4,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 2,
                      "login": "canopen-python"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "1 public repos, account ~1 yr old",
                "points": 5,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 1
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 1
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 91,
            "inputs": {
              "packages": [
                "canopen"
              ],
              "ecosystems": "pypi",
              "any_deprecated": false,
              "min_days_since_publish": 355
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on pypi",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 355 days ago",
                "points": 26,
                "status": "partial",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 355
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "42 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 42
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 81,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 68,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "excellent",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "can",
                "canopen",
                "python"
              ],
              "has_wiki": true,
              "homepage": "http://canopen.readthedocs.io/",
              "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": "http://canopen.readthedocs.io/",
                "points": 15,
                "status": "met",
                "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": "3 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 64,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Branch-Protection, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "branch_protection",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 55,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 5.5
            },
            "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": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 19/25 approved changesets -- score normalized to 7",
                "points": 5.2,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 11 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": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 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 not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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": "1 existing vulnerabilities detected",
                "points": 6.8,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "excellent",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the pypi:canopen@2.4.1 runtime dependency closure — what installing the published package pulls in — 4 packages. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_published",
                "params": {
                  "package": "pypi:canopen@2.4.1",
                  "assessed": 4
                }
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 0,
              "affected_packages": 0,
              "assessed_packages": 4,
              "unassessed_packages": 0,
              "affected_by_severity": "none",
              "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": "no indirect dependency carries a known advisory",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "no_indirect_advisories",
                    "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": 4,
              "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": 10
            },
            "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": 51,
        "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.91,
              "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": "91 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 91,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 51,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [
                "doc/Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [
                "canopen/py.typed"
              ],
              "agent_commit_share": 0,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "doc/Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "doc/Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "canopen/py.typed",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "canopen/py.typed"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "good",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 40222,
              "source_files_sampled": 49,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (canopen/py.typed)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "canopen/py.typed",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/49 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 49,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "examples"
              ],
              "has_mcp_signal": false,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "examples",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "examples"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-26T21:17:41.882771Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/canopen-python/canopen.svg",
  "full_name": "canopen-python/canopen",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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