Öffentliches Register
Software-GesundheitsberichtSchema 0.27.0 · Metriken 1.13.0 · 2026-07-26 21:17 UTC

canopen-python / canopen

CANopen for Python

PythonMIT★ 562 Sterne⑂ 359 Forksseit Sept. 2016Auf GitHub ansehen ↗

canopen-python/canopen erreicht einen Gesundheitsindex von 69 von 100 und liegt damit im Bereich Mittel. Am stärksten schneidet es bei Engineering Quality (81/100) ab, am schwächsten bei AI Readiness (51/100). Zuletzt vor 28 Tagen aktualisiert. 2 Mitwirkende tragen den Großteil der jüngsten Arbeit.

69
gesamt / 100
Mittel

Software-Gesundheitsindex

Metriken werden auf einer Skala von 1–100 in gewichtete Kategorien gruppiert. Der Gesamtwert beginnt als ihr Mittel; sobald öffentliche Evidenz die Richtlinie für Hochrisikojurisdiktionen auslöst, wird die Bewertung angepasst und erhält die Obergrenze 49 (Gefährdet). AI Readiness liegt außerhalb.

69
Exzellent85-100Vorbildlich; erfüllt im Wesentlichen alle geprüften Kriterien
Gut70-84Gesund; geringfügige Lücken
Mittel50-69Akzeptabel mit deutlichen Lücken; Überprüfung empfohlen
Gefährdet30-49Erhebliche Schwächen; eine Übernahme erfordert Vorsicht
Kritisch1-29Schwerwiegende Probleme (aufgegeben, nur ein Maintainer, keine Hygiene)
VitalitätCommunity &VerbreitungNachhaltigkeit &GovernanceEngineering-QualitätSicherheitAI Readiness

Bewertungsprofil

Jede Achse ist eine Kategorie. Die Form zählt mehr als der Durchschnitt — ein gesundes Projekt füllt die gesamte Fläche, während ein Profil aus Spitzen und Kratern bedeutet, dass Stärke in einer Dimension Risiken in einer anderen verdeckt.

Eigentümerschaft

canopen-pythonOrganisation
2 Follower1 öffentliches Reposeit Feb. 2025

Dieses Repository wird von einer Organisation getragen — geteilte, rechenschaftspflichtige Trägerschaft, die jeden einzelnen Maintainer überdauern kann.

Paket-Ökosysteme

RegistryPaketVersionDownloads / MonatVersionenZuletzt veröffentlicht
PyPIcanopen2.4.1857.51942vor 355 Tagen

Metriken nach Kategorie

Vitalität

Lebt das Projekt — wird Code geschrieben und werden Releases ausgeliefert?

63Mittel · 22 % des Gesamtindex
Wie die Bewertung erfolgt
28.8/36Push-Aktualität — letzter Push vor 28 Tagen
10.4/36Commit-Rhythmus — 15/52 Wochen mit Commits
14.8/18Commit-Volumen — 43 Commits im letzten Jahr
10/10OpenSSF Scorecard: Maintained — 26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
Verwendete Eingangsdaten
commits_last_year43
human_commit_share1
days_since_last_push28
active_weeks_last_year15
Wie die Bewertung erfolgt
27/27Liefert Releases aus — 22 Releases veröffentlicht
16.2/36Release-Aktualität — letztes Release vor 355 Tagen
12.6/27Release-Rhythmus — ein Release etwa alle 237 Tage
0/10OpenSSF Scorecard: Signed-Releases — keine Daten
Verwendete Eingangsdaten
releases_count22
latest_release_tagv2.4.1
releases_from_tagsnein
days_since_latest_release355
mean_days_between_releases237
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): OpenSSF Scorecard: Signed-Releases. Die verbleibenden Gewichte wurden renormalisiert.

Community & Verbreitung

Hat das Projekt Nutzer, Downloads, Aufmerksamkeit und ein einladendes Umfeld für Beitragende?

72Gut · 18 % des Gesamtindex
Wie die Bewertung erfolgt
44.6/60Stars — 562 Stars
21.3/25Forks — 359 Forks
7.8/15Watcher — 26 Watcher
Verwendete Eingangsdaten
forks359
stars562
watchers26
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Wie die Bewertung erfolgt
22.5/22.5README
22.5/22.5Lizenz — anerkannte Lizenz (MIT)
0/18CONTRIBUTING-Leitfaden
0/13.5Verhaltenskodex
0/7.2Issue-Vorlage
0/6.3PR-Vorlage
Verwendete Eingangsdaten
has_readmeja
has_licenseja
has_contributingnein
has_issue_templatenein
has_code_of_conductnein
has_pull_request_templatenein
Wie die Bewertung erfolgt
79.1/80Downloads pro Monat — 857.519 Downloads/Monat über pypi
0/20Abhängige in der Registry — von diesem Ökosystem nicht ausgewiesen
Verwendete Eingangsdaten
packagescanopen
dependents
ecosystemspypi
total_downloads
monthly_downloads857.519
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): Abhängige in der Registry. Die verbleibenden Gewichte wurden renormalisiert.

Nachhaltigkeit & Governance

Überdauert das Projekt die Menschen, die es tragen — Bus-Faktor, Reaktionsfähigkeit, Trägerschaft und Paketpflege?

66Mittel · 24 % des Gesamtindex
Wie die Bewertung erfolgt
25.2/54Bus-Faktor — 2 Beitragende decken die Hälfte aller Commits ab
11.5/22.5Commit-Verteilung — wichtigste beitragende Person verfasste 49 % der Commits
13.5/13.5Breite der Beitragenden — 57 Beitragende
10/10OpenSSF Scorecard: Contributors — project has 11 contributing companies or organizations
Verwendete Eingangsdaten
bus_factor2
contributors_sampled57
top_contributor_share0,488
Wie die Bewertung erfolgt
39.3/46.8Issue-Lösungsquote — 84 % der Issues geschlossen
30.8/38.3PR-Annahme — 230/286 entschiedene PRs gemergt
10.5/15OpenSSF Scorecard: Code-Review — Found 19/25 approved changesets -- score normalized to 7
Verwendete Eingangsdaten
merged_prs230
open_issues53
closed_issues278
issue_closed_ratio0,84
closed_unmerged_prs56
Wie die Bewertung erfolgt
30/30Organisatorische Trägerschaft — im Besitz einer Organisation
0/20Verifizierte Domain
3.4/25Reichweite des Inhabers — 2 Follower von canopen-python
5/25Kontohistorie — 1 öffentliche Repos, Kontoalter ca. 1 Jahre
Verwendete Eingangsdaten
followers2
owner_typeOrganization
is_verified
owner_logincanopen-python
public_repos1
account_age_days517

Paketpflege

91Exzellent
Wie die Bewertung erfolgt
25/25Veröffentlicht & auflösbar — 1 Paket(e) auf pypi
26/35Veröffentlichungsaktualität — letzte Veröffentlichung vor 355 Tagen
20/20Versionshistorie — 42 veröffentlichte Versionen
20/20Nicht veraltet — aktiv, nicht veraltet oder zurückgezogen
Verwendete Eingangsdaten
packagescanopen
ecosystemspypi
any_deprecatednein
min_days_since_publish355

Engineering-Qualität

Sind grundlegende Engineering- und Dokumentationspraktiken vorhanden?

81Gut · 20 % des Gesamtindex
Wie die Bewertung erfolgt
24/24CI-Workflows — 3 Workflow(s)
24/24Tests vorhanden
0/16Linter-Konfiguration
0/9.6Pre-Commit-Hooks
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 23 out of 23 merged PRs checked by a CI test -- score normalized to 10
Verwendete Eingangsdaten
has_cija
has_testsja
has_editorconfignein
has_linter_confignein
has_precommit_confignein

Dokumentation

100Exzellent
Wie die Bewertung erfolgt
30/30README
25/25Dokumentationsverzeichnis
15/15Dokumentations-/Homepage-Site — http://canopen.readthedocs.io/
10/10Repository-Beschreibung
10/10Topics — 3 Topics
10/10Wiki
Verwendete Eingangsdaten
topicscan, canopen, python
has_wikija
homepagehttp://canopen.readthedocs.io/
has_readmeja
has_docs_dirja
has_descriptionja

Sicherheit

Sind die sichtbaren Sicherheits- und Lieferkettenpraktiken belastbar, ohne ungeklärte Exposition gegenüber Hochrisikojurisdiktionen?

64Mittel · 16 % des Gesamtindex
Wie die Bewertung erfolgt
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — keine Daten
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.5Lizenz — 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 — keine Daten
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6.8/7.5Vulnerabilities — 1 existing vulnerabilities detected
Verwendete Eingangsdaten
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate5,5
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): branch_protection, signed_releases. Die verbleibenden Gewichte wurden renormalisiert.
Wie die Bewertung erfolgt
35/35Direkte Abhängigkeiten ohne bekannte Advisories — keine direkte Abhängigkeit trägt ein bekanntes Advisory
25/25Indirekte Abhängigkeiten ohne bekannte Advisories — keine indirekte Abhängigkeit trägt ein bekanntes Advisory
0/40Keine offenen Advisories — kein Advisory trägt ein Veröffentlichungsdatum
Verwendete Eingangsdaten
sourceosv
advisories0
affected_packages0
assessed_packages4
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): Keine offenen Advisories. Die verbleibenden Gewichte wurden renormalisiert. Abgeglichen wurde die Laufzeit-Abhängigkeitshülle von pypi:canopen@2.4.1 — das, was die Installation des veröffentlichten Pakets nach sich zieht — mit 4 Paketen. Erreichbarkeit wird nicht analysiert.

AI Readiness

Wie gut ist das Repository dafür ausgestattet, mit KI-Coding-Agenten entwickelt und gepflegt zu werden? Ein unabhängiges, experimentelles Badge — Gewicht 0,0, es wird eigenständig ausgewiesen und verändert den Gesamt-Gesundheitswert nicht.

51Mittel · 0 % des Gesamtindex
Wie die Bewertung erfolgt
0/45Agentenanweisungen — keine CLAUDE.md / AGENTS.md / Editor-Regeln
0/15Maschinenlesbare Doku (llms.txt)
40/40Lesbare Commit-Historie — 91 von 100 menschlichen Commits benennen ihre Absicht (strukturierter Betreff oder erläuternder Text)
Verwendete Eingangsdaten
has_llms_txtnein
legible_history_share0,91
agent_instruction_files
agent_instruction_max_bytes
Wie die Bewertung erfolgt
18/18Bootstrap mit einem Befehl — doc/Makefile
22/22Automatisierte Tests
0/11Lint-/Format-Konfiguration
11/11Statische Typprüfung — canopen/py.typed
0/10Reproduzierbare Umgebung
0/10Belegte Agentenpraxis — keine von Agenten verfassten Commits unter den letzten 100
0/8Automatisierte Wartung — keine automatisierten Abhängigkeits-Updates beobachtet
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Verwendete Eingangsdaten
has_nixnein
has_testsja
lockfiles
has_dockerfilenein
typed_languagenein
bootstrap_filesdoc/Makefile
has_devcontainernein
has_linter_confignein
typecheck_configscanopen/py.typed
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
Wie die Bewertung erfolgt
27/45Typprüfbarer Code — Python mit Typprüfungs-Konfiguration (canopen/py.typed)
55/55Handhabbare Dateigrößen — 0/49 Quelldateien über 60 KB
Verwendete Eingangsdaten
primary_languagePython
largest_source_bytes40.222
source_files_sampled49
oversized_source_files0
Wie die Bewertung erfolgt
0/40API-Schema (OpenAPI/GraphQL/proto)
0/20MCP-Server
40/40Lauffähige Beispiele — examples
Verwendete Eingangsdaten
example_dirsexamples
has_mcp_signalnein
api_schema_files

Eckdaten

562GitHub-Sterne
57Mitwirkende
43Commits, letzte 12 Monate
28Tage seit letztem Push
22Releases
2Bus-Faktor
53offene Issues
PyPIPaket-Ökosysteme

Warnungen zur Datenerhebung

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

Weitere Details

Stern- und Fork-Verlauf 0 ★ / 359 ⇿
0Sterne
359Forks
20Releases

Wann jeder Stern und Fork hinzugefügt wurde, von GitHub erfasst und nach Tagen gruppiert. Das kumulierte Wachstum steht direkt über den täglichen Zugängen, aus denen es besteht, sodass beide gegeneinander lesbar sind: stetiger organischer Zuwachs sieht ganz anders aus als ein abrupter, kurzlebiger Ausschlag. Wo dieser Unterschied messbar ist, wird er als Wachstumsauthentizität ausgewiesen.

0125250375350272016-102021-082026-06
Major 2Minor 13Patch 5

Jeder Punkt umfasst 9 Tage.

OpenSSF Scorecard 5.5 / 10
5.5Gesamtwert

Unabhängige, werkzeugneutrale Sicherheitsbewertung durch das quelloffene OpenSSF Scorecard. Jede Prüfung honoriert eine Sicherheits-Praxis, nicht das Werkzeug eines bestimmten Anbieters. Prüfungen, die Scorecard nicht ermitteln konnte, sind mit k. A. markiert und vom Sicherheitswert ausgeschlossen (nie als null gezählt).Scorecard v5.5.0 · 2026-07-26 21:17 UTC

10Binary-Artifactsno binaries found in the repo
k. A.Branch-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
k. A.Signed-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
9Vulnerabilities1 existing vulnerabilities detected
Direkte Abhängigkeiten 1
RegistryPaketVersionsvorgabeManifest
PyPIpython-can>= 3.0.0pyproject.toml
Alle Abhängigkeiten 10

Vollständig aufgelöster Abhängigkeitssatz aus dem GitHub-Abhängigkeitsgraphen: 1 direkte und 9 indirekte (transitive) Pakete. Die transitive Hülle ist vollständig, wenn das Repository eine Lockfile eincheckt.

RegistryPaketVersionBeziehung
PyPIpython-candirekt
PyPIcanmatrixindirekt
PyPIfuroindirekt
PyPImypyindirekt
PyPIpytestindirekt
PyPIpytest-covindirekt
PyPIsetuptoolsindirekt
PyPIsetuptools-scmindirekt
PyPIsphinxindirekt
PyPIsphinx-autodoc-typehintsindirekt
Abhängigkeits-Advisories 0

Die Installation von pypi:canopen@2.4.1 zieht 4 Pakete nach sich, direkt und transitiv: 0 tragen bekannte Advisories, davon 0 direkte Abhängigkeiten.

Keine bekannten Advisories betreffen die bewerteten Abhängigkeiten.

Ein Advisory bedeutet, dass die im Abhängigkeitsgraphen erfasste Version in den betroffenen Bereich eines Advisories fällt. Erreichbarkeit wird nicht analysiert, und der Graph enthält Entwicklungs- und Test-Pins — ein Fund kann das Werkzeug betreffen und nicht die ausgelieferte Software.

JSON-Rohbericht maschinenlesbar
{
  "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"
}

Bewertungen sind Signale, keine Garantien. Sie spiegeln öffentlich sichtbare Praxis auf GitHub wider — kein Code-Audit und keine Sicherheitsgarantie.

Fehlende Daten werden ausgeschlossen und die Gewichte neu normiert, nie als null bewertet. Die Methodik ist versioniert und offen: Metriken v1.13.0, Schema v0.27.0 — vollständige Methodik · Metriken-Wiki.

Wie ein einzelnes Ergebnis im Gesamtregister steht: aggregierte StatistikenPyPI.