Öffentliches Register
Software-GesundheitsberichtSchema 0.27.0 · Metriken 1.13.0 · 2026-07-26 19:58 UTC

ttsim-dev / ttsim

The computing engine powering GETTSIM

PythonAGPL-3.0★ 2 Sterne⑂ 0 Forksseit Juli 2025Auf GitHub ansehen ↗

ttsim-dev/ttsim erreicht einen Gesundheitsindex von 70 von 100 und liegt damit im Bereich Gut. Am stärksten schneidet es bei Vitality (86/100) ab, am schwächsten bei Community & Adoption (34/100). Zuletzt heute aktualisiert. 3 Mitwirkende tragen den Großteil der jüngsten Arbeit.

70
gesamt / 100
Gut

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.

70
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

6 Follower7 öffentliche Reposseit Feb. 2025

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

Paket-Ökosysteme

RegistryPaketVersionDownloads / MonatVersionenZuletzt veröffentlichtTags
PyPIttsim-backend1.2.1-10vor 62 Tagenbenefitseconomicspensionstaxestransfers

Metriken nach Kategorie

Vitalität

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

86Exzellent · 22 % des Gesamtindex
Wie die Bewertung erfolgt
36/36Push-Aktualität — letzter Push vor 0 Tagen
15.2/36Commit-Rhythmus — 22/52 Wochen mit Commits
16.2/18Commit-Volumen — 62 Commits im letzten Jahr
10/10OpenSSF Scorecard: Maintained — 18 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
Verwendete Eingangsdaten
commits_last_year62
human_commit_share0,97
days_since_last_push0
active_weeks_last_year22

Release-Disziplin

100Exzellent
Wie die Bewertung erfolgt
27/27Liefert Releases aus — 10 Releases veröffentlicht
36/36Release-Aktualität — letztes Release vor 62 Tagen
27/27Release-Rhythmus — ein Release etwa alle 33,9 Tage
0/10OpenSSF Scorecard: Signed-Releases — keine Daten
Verwendete Eingangsdaten
releases_count10
latest_release_tagv1.2.1
releases_from_tagsnein
days_since_latest_release62
mean_days_between_releases33,9
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?

34Gefährdet · 18 % des Gesamtindex
Wie die Bewertung erfolgt
0/60Stars — 2 Stars
0/25Forks — 0 Forks
0/15Watcher — 2 Watcher
Verwendete Eingangsdaten
forks0
stars2
watchers2
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Wie die Bewertung erfolgt
22.5/22.5README
22.5/22.5Lizenz — anerkannte Lizenz (AGPL-3.0)
0/18CONTRIBUTING-Leitfaden
13.5/13.5Verhaltenskodex
0/7.2Issue-Vorlage
6.3/6.3PR-Vorlage
Verwendete Eingangsdaten
has_readmeja
has_licenseja
has_contributingnein
has_issue_templatenein
has_code_of_conductja
has_pull_request_templateja

Nachhaltigkeit & Governance

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

74Gut · 24 % des Gesamtindex
Wie die Bewertung erfolgt
36/54Bus-Faktor — 3 Beitragende decken die Hälfte aller Commits ab
16.8/22.5Commit-Verteilung — wichtigste beitragende Person verfasste 26 % der Commits
13.5/13.5Breite der Beitragenden — 28 Beitragende
10/10OpenSSF Scorecard: Contributors — project has 16 contributing companies or organizations
Verwendete Eingangsdaten
bus_factor3
contributors_sampled28
top_contributor_share0,255
Wie die Bewertung erfolgt
36.4/46.8Issue-Lösungsquote — 78 % der Issues geschlossen
29.2/38.3PR-Annahme — 81/106 entschiedene PRs gemergt
13.5/15OpenSSF Scorecard: Code-Review — Found 27/30 approved changesets -- score normalized to 9
Verwendete Eingangsdaten
merged_prs81
open_issues8
closed_issues28
issue_closed_ratio0,778
closed_unmerged_prs25
Wie die Bewertung erfolgt
30/30Organisatorische Trägerschaft — im Besitz einer Organisation
0/20Verifizierte Domain
6.1/25Reichweite des Inhabers — 6 Follower von ttsim-dev
9.4/25Kontohistorie — 7 öffentliche Repos, Kontoalter ca. 1 Jahre
Verwendete Eingangsdaten
followers6
owner_typeOrganization
is_verified
owner_loginttsim-dev
public_repos7
account_age_days516

Paketpflege

100Exzellent
Wie die Bewertung erfolgt
25/25Veröffentlicht & auflösbar — 1 Paket(e) auf pypi
35/35Veröffentlichungsaktualität — letzte Veröffentlichung vor 62 Tagen
20/20Versionshistorie — 10 veröffentlichte Versionen
20/20Nicht veraltet — aktiv, nicht veraltet oder zurückgezogen
Verwendete Eingangsdaten
packagesttsim-backend
ecosystemspypi
any_deprecatednein
min_days_since_publish62

Engineering-Qualität

Sind grundlegende Engineering- und Dokumentationspraktiken vorhanden?

78Gut · 20 % des Gesamtindex
Wie die Bewertung erfolgt
24/24CI-Workflows — 2 Workflow(s)
24/24Tests vorhanden
16/16Linter-Konfiguration
9.6/9.6Pre-Commit-Hooks
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 28 out of 28 merged PRs checked by a CI test -- score normalized to 10
Verwendete Eingangsdaten
has_cija
has_testsja
has_editorconfignein
has_linter_configja
has_precommit_configja
Wie die Bewertung erfolgt
30/30README
0/25Dokumentationsverzeichnis
15/15Dokumentations-/Homepage-Site — https://gettsim.readthedocs.io/
10/10Repository-Beschreibung
0/10Topics
0/10Wiki
Verwendete Eingangsdaten
topics
has_wikinein
homepagehttps://gettsim.readthedocs.io/
has_readmeja
has_docs_dirnein
has_descriptionja

Sicherheit

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

73Gut · 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 — 28 out of 28 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
6.8/7.5Code-Review — Found 27/30 approved changesets -- score normalized to 9
2.5/2.5Contributors — project has 16 contributing companies or organizations
10/10Dangerous-Workflow — no dangerous workflow patterns detected
7.5/7.5Dependency-Update-Tool — update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Lizenz — license file detected
7.5/7.5Maintained — 18 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
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Verwendete Eingangsdaten
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate6,6
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_packages49
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:ttsim-backend@1.2.1 — das, was die Installation des veröffentlichten Pakets nach sich zieht — mit 49 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.

60Mittel · 0 % des Gesamtindex
Wie die Bewertung erfolgt
45/45Agentenanweisungen — AGENTS.md, CLAUDE.md, GEMINI.md
0/15Maschinenlesbare Doku (llms.txt)
40/40Lesbare Commit-Historie — 74 von 97 menschlichen Commits benennen ihre Absicht (strukturierter Betreff oder erläuternder Text)
Verwendete Eingangsdaten
has_llms_txtnein
legible_history_share0,763
agent_instruction_filesAGENTS.md, CLAUDE.md, GEMINI.md
agent_instruction_max_bytes6.112
Wie die Bewertung erfolgt
0/18Bootstrap mit einem Befehl
22/22Automatisierte Tests
11/11Lint-/Format-Konfiguration
0/11Statische Typprüfung
0/10Reproduzierbare Umgebung
10/10Belegte Agentenpraxis — 23 der letzten 100 Commits von Agenten verfasst oder ihnen zugeschrieben
8/8Automatisierte Wartung — 2 der letzten 100 Commits sind automatisierte Abhängigkeits-Updates
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_files
has_devcontainernein
has_linter_configja
typecheck_configs
agent_commit_share0,23
toolchain_manifests
dependency_bot_commit_share0,02
Wie die Bewertung erfolgt
0/45Typprüfbarer Code — Python ohne Typprüfungs-Konfiguration
54.5/55Handhabbare Dateigrößen — 1/115 Quelldateien über 60 KB
Verwendete Eingangsdaten
primary_languagePython
largest_source_bytes61.629
source_files_sampled115
oversized_source_files1
Wie die Bewertung erfolgt
0/40API-Schema (OpenAPI/GraphQL/proto)
0/20MCP-Server
40/40Lauffähige Beispiele — notebooks
Verwendete Eingangsdaten
example_dirsnotebooks
has_mcp_signalnein
api_schema_files

Eckdaten

2GitHub-Sterne
28Mitwirkende
62Commits, letzte 12 Monate
0Tage seit letztem Push
10Releases
3Bus-Faktor
8offene Issues
PyPIPaket-Ökosysteme

Warnungen zur Datenerhebung

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • Could not fetch pypi package 'mettsim' from its registry
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Weitere Details

OpenSSF Scorecard 6.6 / 10
6.6Gesamtwert

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 19:58 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-Tests28 out of 28 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
9Code-ReviewFound 27/30 approved changesets -- score normalized to 9
10Contributorsproject has 16 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained18 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
10Vulnerabilities0 existing vulnerabilities detected
Direkte Abhängigkeiten 17
RegistryPaketVersionsvorgabeManifest
PyPIbeartype>=0.18pyproject.toml
PyPIdags>=0.6pyproject.toml
PyPIipywidgetspyproject.toml
PyPIjaxtyping>=0.3.10pyproject.toml
PyPIkaleido>=1.1pyproject.toml
PyPInetworkx>=3.5pyproject.toml
PyPInumpypyproject.toml
PyPInumpy-groupiespyproject.toml
PyPIopenpyxlpyproject.toml
PyPIoptree>=0.16pyproject.toml
PyPIpandas>=3pyproject.toml
PyPIplotly>=6.5pyproject.toml
PyPIportionpyproject.toml
PyPIpygmentspyproject.toml
PyPIpygraphvizpyproject.toml
PyPIpytestpyproject.toml
PyPIpyyamlpyproject.toml
Alle Abhängigkeiten nicht erhoben

Der aufgelöste Abhängigkeitssatz konnte für diesen Bericht nicht erhoben werden: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Abhängigkeits-Advisories 0

Die Installation von pypi:ttsim-backend@1.2.1 zieht 49 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": [],
      "is_fork": false,
      "size_kb": 57157,
      "has_wiki": false,
      "homepage": "https://gettsim.readthedocs.io/",
      "languages": {
        "Python": 1005595,
        "Jupyter Notebook": 2152
      },
      "pushed_at": "2026-07-26T09:26:24Z",
      "created_at": "2025-07-24T07:06:26Z",
      "owner_type": "Organization",
      "updated_at": "2026-06-24T18:20:06Z",
      "description": "The computing engine powering GETTSIM",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "AGPL-3.0",
      "default_branch": "main",
      "license_spdx_raw": "AGPL-3.0",
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": "https://gettsim.readthedocs.io/",
      "name": "Taxes & Transfers SIMulators",
      "type": "Organization",
      "login": "ttsim-dev",
      "company": null,
      "location": null,
      "followers": 6,
      "avatar_url": "https://avatars.githubusercontent.com/u/200745588?v=4",
      "created_at": "2025-02-25T09:09:59Z",
      "is_verified": null,
      "public_repos": 7,
      "account_age_days": 516
    },
    "license": {
      "state": "standard",
      "spdx_id": "AGPL-3.0",
      "raw_spdx": "AGPL-3.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v1.2.1",
          "kind": "patch",
          "published_at": "2026-05-25T11:01:22Z"
        },
        {
          "tag": "v1.2.0",
          "kind": "minor",
          "published_at": "2026-03-19T14:45:04Z"
        },
        {
          "tag": "v1.1.1",
          "kind": "patch",
          "published_at": "2026-01-12T14:31:22Z"
        },
        {
          "tag": "v1.1.0",
          "kind": "minor",
          "published_at": "2025-08-26T09:52:01Z"
        },
        {
          "tag": "v1.0.0",
          "kind": "major",
          "published_at": "2025-08-09T18:59:40Z"
        },
        {
          "tag": "v1.0a4",
          "kind": "other",
          "published_at": "2025-08-09T06:10:04Z"
        },
        {
          "tag": "v1.0a3",
          "kind": "other",
          "published_at": "2025-07-27T13:52:14Z"
        },
        {
          "tag": "v1.0a2",
          "kind": "other",
          "published_at": "2025-07-26T11:24:26Z"
        },
        {
          "tag": "v1.0a1",
          "kind": "other",
          "published_at": "2025-07-24T14:05:31Z"
        },
        {
          "tag": "v1.0a",
          "kind": "other",
          "published_at": "2025-07-24T13:53:03Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "0fdc84b1753d52b3a35015a966dcb250d98c39aa",
          "body": "`.ai-instructions` + prek-hook bump, plus the code changes ty `0.0.52`\nrequires.\n\n- `.ai-instructions` submodule → `68dc3f4`\n- prek hooks refreshed (ruff `v0.15.19`, pyproject-fmt `v2.25.0`, ty\n`v0.0.52`)\n- ty 0.0.52 fixes: `tt/vectorization.py` and\n  `interface_dag_elements/policy_environment.py`\n-\n[…]\nstays at `v0.0.52` — the latest **published** ty binary (the\n`ty-pre-commit`\n`v0.0.53` tag has no corresponding `astral-sh/ty` release).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)",
          "is_bot": false,
          "headline": "Bump .ai-instructions to 68dc3f4 + adapt to ty 0.0.52 (#135)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-24T18:20:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b86b0d6e66d7ffaf1002b093fa8ea70f0a0e862e",
          "body": "…k (#133)\n\n## Summary\n\nty now runs only as pre-commit hooks, under both backends, replacing the\ndedicated pixi `type-checking`/`type-checking-jax` features,\nenvironments, and tasks (`pixi run ty` / `pixi run ty-jax` no longer\nexist).\n\n- **numpy run**: the official\n[astral-sh/ty-pre-commit](https://g\n[…]\nsed, 42\nskipped\n- [x] `pixi lock` — lock file validates after pruning\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Replace hand-written ty machinery with the official ty-pre-commit hoo…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-15T19:04:41Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "cc271726d91192997e2dd090c47bd585c168e2e5",
          "body": "### What problem do you want to solve?\n\nSome guards are used in GETTSIM but not here. Added examples to METTSIM\nand the corresponding ignores to pyproject.toml.",
          "is_bot": false,
          "headline": "Restore PLR vectorization guard (#132)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-06-09T12:37:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fceea5e64202bcbf15bfec05b75e6939da419dc3",
          "body": "…with_original_p_ids (#131)\n\nCloses #130.\n\n- Split `raw_results.columns` into\n`raw_results.columns_with_internal_p_ids` (computable from\n`processed_data` alone, endogenous `p_id_*` columns hold internal\nindices) and `raw_results.columns_with_original_p_ids` (the\nreverse-translation from #108, consum\n[…]\ned with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Split raw_results.columns into columns_with_internal_p_ids / columns_…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-06-07T11:57:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "280694713774da873f7073a6df0012d064e0e1b0",
          "body": "…tsim (#129)\n\nThree related changes, following the discovery in\nhttps://github.com/ttsim-dev/gettsim/pull/1178#discussion_r3366916727\nthat the vectorizer handles ternary expressions (`ast.IfExp` →\n`xnp.where` via `_ifexp_to_call`):\n\n1. **Remove the dead SIM108 ignore** for\n`src_mettsim/mettsim/middl\n[…]\nweep for gettsim's policy code: ttsim-dev/gettsim#1195.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Remove dead SIM108 ignore for middle_earth; showcase ternaries in met…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-06-07T09:15:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ad3d21c122e1f73184f0113d602e7b73e4b30119",
          "body": "…#127)\n\n`load_policy_cases` in `test_mettsim.py` resolved the policy-cases root\nas `ROOT_PATH.parent / \"tests_middle_earth\" / \"policy_cases\"`, i.e.\n`src_mettsim/mettsim/tests_middle_earth/...`, which does not exist. The\nglob found nothing, `POLICY_TEST_IDS_AND_CASES` was empty, and\n`test_policy_case\n[…]\n` — 32\npassed (24 policy cases now collected and green)\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Fix mettsim policy-case collection: all cases were silently skipped (…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-06-06T07:26:55Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "dfc457df33e58d767706eec3c9ae880c12750f22",
          "body": "## Summary\n\nSweep: every call to a project-defined function/method/class with 2+\npositional args is rewritten to use keyword names.\n\nScope:\n- src/ttsim, tests, src_mettsim\n- Project-internal targets only — stdlib, numpy, pandas, jax, and other\nthird-party calls are left alone.\n- The resolver works p\n[…]\ner <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "chore: keyword args for project-internal multi-arg calls (#114)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-03T14:10:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "eb304073b23c73a59dc61af5eff61ff2fa3bcb38",
          "body": "## Summary\n\nFill in the six `*_by_p_id` aggregation primitives that were previously\n`raise NotImplementedError` in both `ttsim/tt/aggregation_numpy.py` and\n`ttsim/tt/aggregation_jax.py`. `sum_by_p_id` was the only kind\nimplemented before; everything else surfaced as `NotImplementedError` to\ncallers.\n[…]\ner <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Implement count/mean/max/min/any/all by_p_id aggregations (#110)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-03T14:06:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4351a458eb5ba1f1302a043540f57e20c6ec3cd9",
          "body": "…) (#108)\n\n## Summary\n- `raw_results.columns` now reverse-translates any output column whose\nleaf name starts with `p_id_` from internal indices back to user-space\n`p_id` values, preserving the `-1` no-link sentinel.\n- Removes the previously protective\n`fail_if.endogenous_p_id_among_targets` (and it\n[…]\ner <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Reverse-translate endogenous p_id_* targets to user p_ids (closes #43…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-03T14:03:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7d7530ac3c23777d64921248fd5b8a1047eeb77c",
          "body": "…s (#113)\n\n## What\n\nScalar inputs feeding functions that are **not** auto-vectorized —\naggregations, group-creation functions, and\n`vectorization_strategy=\"not_required\"` policy functions — are now\nbroadcast to length `n_obs` at **call time**, instead of raising a\nfail-fast error. Auto-vectorized fu\n[…]\n.7 <noreply@anthropic.com>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Defer scalar→array broadcast to call time for non-vectorized function…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-06-02T17:02:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b781ec81523c8114192eb3edad794d7eaf69e49f",
          "body": "## Summary\n- Closes #84.\n- Expands the module docstring in ``src/ttsim/typing.py`` to capture why\n``TYPE_CHECKING`` + ``from __future__ import annotations`` must stay in\nplace across the codebase: ``jaxtyping.Array`` resolves via a top-level\n``__getattr__`` hook that imports JAX, so any module touch\n[…]\nuthored-by: Claude Opus 4.7 <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Document JAX-optional constraint on typing module (#84) (#106)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T16:14:27Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a4cc9c5e0cb87104fb3b61150f08a86a13290d23",
          "body": "…105)\n\n## Summary\n- Closes #73.\n- Cause: ``load_module`` registered policy modules in ``sys.modules``\nunder a *root-relative* short name (e.g.\n``orc_hunting_bounty.orc_hunting_bounty``). Decorated objects'\n``__module__`` then pointed at a non-importable name, so cloudpickle's\npickle-by-reference mac\n[…]\nuthored-by: Claude Opus 4.7 <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Load policy modules under their canonical Python import name (#73) (#…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T16:09:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ca81551cc6ffe6e64760f092386fd99f6c1f1247",
          "body": "…79) (#104)\n\n## Summary\n- Closes #79.\n- ``with_processed_params_and_scalars``\n(``specialized_environment.py:152``) used to only insert scalars from\n``processed_data`` when the qname was already a key in\n``without_tree_logic_and_with_derived_functions``. The time-conversion\nmachinery builds conversio\n[…]\nuthored-by: Claude Opus 4.7 <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Partial scalar inputs that drive derived time-conversion functions (#…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T16:04:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "368cdf3ef458a83edbbb98e6aac7114c67fa0827",
          "body": "## Summary\n\nMirror `results.df_with_qname_columns` with the input-side counterpart\nso the qname format works in both directions.\n\n- `InputData.qname({\"a__b\": ...})` becomes a live factory. Previously\nthe factory existed in the public API but no interface function consumed\n`input_data__qname`, so cal\n[…]\nuthored-by: Claude Opus 4.7 <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Add symmetric qname I/O for InputData and results (#103)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T15:59:54Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "747bd3eeec944db5e5315a145f0a17024c57fd32",
          "body": "…loses #97, #94) (#102)\n\n## Summary\n\nA single ``_canonicalize_input_dtype`` helper in ``processed_data.py``\nnow normalizes every kind of non-canonical input column to numpy:\n\n- **Unsigned integers** (numpy uint, pandas-nullable ``UInt*``,\n``uint*[pyarrow]``) → ``int64`` (``int32`` on jax with x64 di\n[…]\nuthored-by: Claude Opus 4.7 <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Canonicalize input dtypes; coerce uint, normalize nullable/pyarrow (c…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T15:55:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "0fabd06c89aa6c928df5ca3a7f361e69e306da15",
          "body": "Implements GEP 9: package-wide beartype claw on by default, TTSIM_BEARTYPE_CLAW=0 as a full opt-out (O0-strategy confs gate perimeter decorators + forwarder), TTSIMError hierarchy, wide/narrow UserX type split. See PR #101.",
          "is_bot": false,
          "headline": "Beartype + jaxtyping runtime type-checking rollout (GEP 9) (#101)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-06-02T15:15:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4d02ac8665f2220053a583e0ea40a3a0351065c3",
          "body": "Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Prepare release 1.2.1 (#112)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-05-25T10:58:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c30f66d24c84bf4106ceb753cd0f47e95a054964",
          "body": "## Coordinated change\n\nPart of a three-repo change adopting runtime type checking in dags-based\nprojects:\n\n-\n**[OpenSourceEconomics/dags#82](https://github.com/OpenSourceEconomics/dags/pull/82)**\n— makes dags wrappers honest forwarders so runtime checkers aren't\nmisled. Keystone; releases as dags 0.\n[…]\nlf. The forward-compatible half\nis exercised by dags#82's own suite.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.7 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Adapt to dags' runtime-type-checker-friendly wrappers (#99)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-05-24T10:03:42Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8ecbaca5462925442afe795e67408a355266e9cb",
          "body": "## Summary\n\nVectorisation strategy `\"loop\"` failed if functions had piecewise\npolynomial param inputs. This PR fixes this.\n\nRelated to #91 — these fixes enable downstream projects (e.g. GETTSIM)\nto switch policy functions to the `\"loop\"` strategy for any (not already\nnatively) vectorised function.\n\n\n[…]\n resolve type\nannotations on `numpy.vectorize` objects.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Fix loop vectorization for functions with param helpers (#92)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-03-23T15:11:35Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6fd0123d160a1e32604b71f8e3141c8b87c76872",
          "body": null,
          "is_bot": false,
          "headline": "Change title in changelog for release.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-03-19T14:40:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5cf4228eb23912616f52f05160a78a495af60160",
          "body": null,
          "is_bot": false,
          "headline": "Prepare CHANGES.md for release.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-03-19T14:25:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61ef297df75fa69ff6c7b8ad9ecc514324546f35",
          "body": "- make a dataclass frozen\n- add dags.md to ai-instructions\n- bump pyproject-fmt and apply",
          "is_bot": false,
          "headline": "Trivial updates to code quality. (#90)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-03-19T14:09:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1c17c1f5ff36cb5d4ca924dfbf336ef1ee3ae62f",
          "body": "## Summary\n- Convert all NumPy-style `Parameters`/`Returns`/`Raises` docstring\nsections to Google convention across 15 infrastructure files\n- Drop type annotations from docstrings (already in signatures)\n- Net: -182 lines (more concise format)\n\nCloses #88\n\n## Test plan\n- [x] `pixi run ty` — all chec\n[…]\nGenerated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Migrate docstrings from NumPy to Google convention (#89)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-03-19T11:08:46Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9bbba34d26703f5f8febffaa2ff7d4d8397f44ed",
          "body": "…08) (#76)\n\nImplemt GEP-08.\n\nReplace numbered dict format with ordered list of interval dicts:\n- Add interval_utils.py: validate_intervals(),\nextend_intervals_to_real_line()\n- Support partial domains (NaN for out-of-domain values)\n- Transpose rates shape from (n_coefficients, n_intervals) to\n(n_inte\n[…]\nuthored-by: Claude Opus 4.5 <noreply@anthropic.com>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Refactor piecewise polynomial to use explicit interval notation (GEP-…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-03-19T05:46:08Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9e2aad237bf7da831db576b1cc1cef6fa20a9b3a",
          "body": "`dags` 0.5.0 includes a couple of changes, which would raise\n`DeprecationWarning`s and at some point break `ttsim`:\n\n- `fail_if_paths_are_invalid` has been removed from `dags.tree`\nnamespace, must import via `from dags.tree.validation import\nfail_if_paths_are_invalid`\n- `one_function_without_tree_logic` has been renamed to\n`get_one_function_without_tree_logic`\n\nWe fix those imports here.",
          "is_bot": false,
          "headline": "Require `dags` 0.5.0 (#86)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-03-11T10:08:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "100477aa33a0915fd919f3776b3a537e705285fe",
          "body": "### What problem do you want to solve?\n\nInternally, we need string annotations (via `from __future__ import\nannotations`) and `if TYPE_CHECKING` blocks in order to not require jax\nas a runtime dependency:\n\n- Bool, Float, Int (dtype validators) can be imported without JAX\n- Array cannot - it triggers\n[…]\nrization machinery. Changing two lines helps.\n\n---------\n\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>\nCo-authored-by: Marvin Immesberger <74215010+MImmesberger@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Allow non-string annotations in vectorization. (#83)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-02-12T18:25:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fd099009d29b78670c92923435d324becb515983",
          "body": "## Summary\n- Add a `run-ty` job to the main CI workflow that runs the `ty` static\ntype checker\n- Runs on `ubuntu-latest` using the `py314-jax` pixi environment, in\nparallel with existing test jobs\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add ty static type checker to CI (#82)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-02-12T18:07:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e647ffe16f221ecd438d212d943bb095e6922c75",
          "body": "### What problem do you want to solve?\n\nCloses #18 \n\n- Add extensive test coverage for interface layer modules\n- test_interface_node_objects.py - Tests for interface DAG node objects\n- test_dates.py - Tests for date handling functionality\n- test_labels.py - Extended label tests\n- test_tt_targets.py \n[…]\n.5 <noreply@anthropic.com>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Improve test coverage (#75)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-02-12T10:46:13Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "0397cba2c5798215db3f14442f3c149673cb6416",
          "body": "Fix #80.\n\n*(curse of dimensionality at work, sorry...)*\n\n---------\n\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Generalise type hints of time conversion functions (#81)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-02-11T21:23:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d36b549d83731bcb0a4d145f6fc48027101a6a1b",
          "body": "Move ty to test feature and other updates following the new template.\n\n---------\n\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Boilerplate update (#74)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2026-01-28T10:45:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2e26d3ff97725d593db9641f9b9b1ddd0349384d",
          "body": "- Ran\n[code-simplifier](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-simplifier)\non the codebase.\n- Use `pyproject.toml` etc. based on https://github.com/OpenSourceEconomics/ai-instructions/tree/main/boilerplate\n\n---------\n\nCo-authored-by: Marvin Immesberger <74215010+MImmesberger@users.noreply.github.com>\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Simplify, update boilerplate code (#69)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-01-23T16:56:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eef66a63166c0b3478489f102b68863f0af8968c",
          "body": "Fix #58",
          "is_bot": false,
          "headline": "Get rid of UnitTest style tests. (#68)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-01-21T13:37:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "51f3d8d5de6e7751664339fa3d6376a22f2bd55b",
          "body": "Fix #45 using standard glob patterns:\n\n| Pattern element | Meaning |\n|-----------------|---------|\n| `\"exact_name\"` | Matches this exact path segment |\n| `\"prefix*\"` | Matches segments starting with `prefix` |\n| `\"*suffix\"` | Matches segments ending with `suffix` |\n| `\"*infix*\"` | Matches segments c\n[…]\n](https://gettsim--1128.org.readthedocs.build/en/1128/tutorials/visualizing_the_system.html#Glob-patterns-for-flexible-matching).\n- Twin [GETTSIM #1128](https://github.com/ttsim-dev/gettsim/pull/1128)",
          "is_bot": false,
          "headline": "Better selection mechanism for `node.colormap` in graphs. (#67)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-01-18T08:08:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0768ada5a637472912a242cd9c09ee75e9c2eb13",
          "body": "Requires a fix on the dags side in 0.4.2 (see [dags #59](https://github.com/OpenSourceEconomics/dags/pull/59) that popped up in GETTSIM, add a reproducer here.\n\n---------\n\nCo-authored-by: Claude Opus 4.5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add Python 3.14 support. (#65)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2026-01-08T17:58:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8a07b104230a0e4eefa481fe70eb67e781f12791",
          "body": "Lots of improvements, highlights:\n\n- The typing of `OrigParamSpec` was off. What we had written was \"one of\ntwo exclusive kinds of dictionaries\" where we wanted to write \"One\ndictionaries where key/value pairs can take one of two forms\". Fixed via\na Protocol.\n- Use `TypeAlias` for `OrderedQNames` in\n[…]\nsted_nodes_cannot_be_found` to \n`_fail_if_requested_nodes_not_in_dag` and do not do redundant check on\ninput-dependent interface functions (made a test out of that).\n\n---\n- co-authoed by @MImmesberger",
          "is_bot": false,
          "headline": "Move from mypy to ty (#63)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-12-23T14:25:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9c7c7f736d4c8a0bad208be8e77e640b9b4d9cba",
          "body": "- Update pixi in GHA.\n- Wrap parsing of options in try-except block so we can run tests in all repos included in dev-gettsim workspace.",
          "is_bot": false,
          "headline": "Some chores (#62)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-12-21T09:22:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "855816a0babe1ebbc90b629f97bb8b07f040dba5",
          "body": "<!--pre-commit.ci start-->\nupdates:\n- [github.com/lyz-code/yamlfix: 1.17.0 →\n1.19.0](https://github.com/lyz-code/yamlfix/compare/1.17.0...1.19.0)\n- [github.com/astral-sh/ruff-pre-commit: v0.12.11 →\nv0.14.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.11...v0.14.7)\n- [github.com/kynan/\n[…]\n0.35.0)\n<!--pre-commit.ci end-->\n\n---------\n\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": true,
          "headline": "[pre-commit.ci] pre-commit autoupdate (#59)",
          "author_name": "pre-commit-ci[bot]",
          "author_login": "pre-commit-ci[bot]",
          "committed_at": "2025-12-16T13:03:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b1bd853bcc388387813643cda900d36e80b15fc9",
          "body": "Bumps the github-actions group with 3 updates in the / directory:\n[actions/checkout](https://github.com/actions/checkout),\n[prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) and\n[actions/setup-python](https://github.com/actions/setup-python).\n\nUpdates `actions/checkout` from 5 to 6\n<d\n[…]\n of the specified dependency and ignore\nconditions\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "Bump the github-actions group across 1 directory with 3 updates (#61)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2025-12-16T11:49:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6d3482c1429f0529a709d65badfcb48ca3c5eb67",
          "body": "### What problem do you want to solve?\n\nThis PR\n- renames time converters for flow variables (e.g. earnings **per**\nmonth) from `x_to_y` to `per_x_to_per_y` with x, y being our usual time\nabbreviations.\n- adds time converters for stock variables (e.g. number of unemployment\nperiods on the monthly **level**) named `x_to_y`\n- exposes the stock time converters to make them importable via\n`ttsim.interface_dag_elements`.",
          "is_bot": false,
          "headline": "Add and expose time converters targeting stocks (#56)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-10-02T07:53:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "755304bcde926bef7b12f9a8bea8573450d04fb8",
          "body": "### What problem do you want to solve?\n\nThe _q to _m automatic time conversion (and the corresponding test) was\nincorrect. This PR fixes that.",
          "is_bot": false,
          "headline": "Fix _q to _m time conversion. (#55)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-09-15T07:50:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "305f4f8d003b14e6781b7a96f0e7d8aa3d60d6af",
          "body": "### What problem do you want to solve?\n\nAdds\n\n- a `fail_if` function that raises an error if a function expects an\narray as an input (because it is natively vectorized), but got passed a\nscalar via `processed_data`\n- a `warn_if` function that raises a warning if\n`tt_function_set_annotations` is `False` (because this disables the\ncheck above).\n\n---------\n\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Raise error if scalar is passed to function expecting an array (#53)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-09-06T15:07:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "591358e1baa7ac950a4ab00da64c35b30ef47a14",
          "body": "…d an ambiguous name. (#52)\n\n### What problem do you want to solve?\n\n- `fail_if__input_data_tree_is_invalid` was too strict because it\nallowed for array leafs only. It now also allows for float/int/bool.\nAlso fixed some corresponding bugs down the line.\n- Rename `environment_is_invalid` to `policy_environment_is_invalid` to\nmake clear that this is not about the specialized policy environment.\n\n---------\n\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Fix fail_if checks: input data tree was too greedy and environment ha…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-09-04T12:25:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b5d8a6ebf7640ad8dc1a5b289e6e02f476d42a56",
          "body": "Bumps the github-actions group with 2 updates:\n[actions/checkout](https://github.com/actions/checkout) and\n[prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi).\n\nUpdates `actions/checkout` from 4 to 5\n<details>\n<summary>Release notes</summary>\n<p><em>Sourced from <a\nhref=\"https://github\n[…]\n of the specified dependency and ignore\nconditions\n\n\n</details>\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "Bump the github-actions group with 2 updates (#50)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2025-09-01T07:24:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b08f2e1f61badf375f9d3711a595db927b39e5df",
          "body": null,
          "is_bot": false,
          "headline": "Typo spotted by @JuergenWiemers",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-08-26T16:34:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1fda301ebd80577187e2ec1eeb3360707f9699c1",
          "body": "Closes #47",
          "is_bot": false,
          "headline": "Fix test failure on WSL2 due to different DAG execution order (#49)",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-26T16:20:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6404bd3ff65e8ed934a93daf9d7e58209265f092",
          "body": "Just updated the heading for v1.1.0",
          "is_bot": false,
          "headline": "Fix changelog ex post. (#48)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-08-26T09:54:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d0942d8f25fbd2dc5f97b6007796e95147d2b13c",
          "body": "### What problem do you want to solve?\n\nSee discussion in [GETTSIM\n#1073](https://github.com/ttsim-dev/gettsim/issues/1073). Currently, the\ncycler will pick different colors for nodes depending on the contents of\ngraph.\n\nThis PR allows specifying a mapping of tree paths to colours in both\n`plot.dag`\n[…]\nupsert_tree` and `merge_trees`.\n- Re-ordered the arguments to 'main' into blocks: [typical user input]\n[rough interface dag flow]\n\n---------\n\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Plots with colormap and improved descriptions (#44)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-08-26T09:45:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "788b727a40da7bf8aadc9803cd6693e87d62334a",
          "body": "Small optimization. Considerable impact if `include_fail_nodes=True` and\nthe input data is large.",
          "is_bot": false,
          "headline": "Optimization for `fail_if.foreign_keys_are_invalid_in_data` (#46)",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-21T16:51:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "83de7f68f1acd62bcaf6b920b49864edd48b616c",
          "body": "This PR pre-sorts user data by original person ID (`p_id`) to improve\nperformance.\n\n### Key Changes:\n\n- User data is now sorted by original `p_id` early in the pipeline\n(`processed_data`)\n- Introduced a new `sort_indices` interface function that computes\nsorting indices for restoring original row or\n[…]\ner endogenously computed\n`p_id`s are included in the target list, opened #43 related to that\n- Added tests for row order restoration functionality\n- Updated existing tests to work with the sorted data",
          "is_bot": false,
          "headline": "Pre-sort user data by p_id for performance (#42)",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-20T08:21:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8d88fa76958e76c8402840d628045c915a0e343d",
          "body": "…s.fail_if` functions (#41)\n\n- The crucial change is in `tt.shared.join`. It looks like we currently\nuse an NxN lookup table; we really shouldn't do that. 😅With this PR,\nruntime complexity is $O(log(n))$ instead of $O(n)$ and memory usage is\n$O(n)$ instead of $O(n^2)$.\n- The optimizations in the `fail_if` functions mostly help the\n\"pre-processing stage\".",
          "is_bot": false,
          "headline": "Optimizations: `tt.shared.join`, several `ttsim.interface_dag_element…",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-14T08:12:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ad625be4b02aad01e7ffacbacbf09dfec85589a3",
          "body": "Optimize `aggregation_numpy.sum_by_p_id` and small optimization of\n`data_converters.df_with_mapped_columns_to_flat_data`\n\n- I am still trying to pick some low hanging (optimization) fruits. I am\nreaching the point of diminishing returns (which is good!).\n- I tried to be minimally invasive. I only re\n[…]\n7     1.22x       Complete execution                   \n--------------------------------------------------------------------------------------------------------------------------------------------\n```",
          "is_bot": false,
          "headline": "Improve performance of `aggregation_numpy` and `data_converters` (#40)",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-12T05:15:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "97dea8a9add9dd8218c5f8c494b388c150d9b7f7",
          "body": "### What problem do you want to solve?\n\nThe `all_paths` selection type shows all paths that connect any primary\nnodes. There must be at least two primary nodes.",
          "is_bot": false,
          "headline": "Plotting: Replace `nodes` selection type with `all_paths` (#38)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-08-09T18:54:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bae8342b06708de6fcbf88602bffaf4ebea5bedb",
          "body": "### What problem do you want to solve?\n\nCloses #33\n\nAlso adds `SpecializedEnvironmentForPlottingAndTemplates` as an argument\nto `main`.\n\n---------\n\nCo-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "Pass all arguments of main via class methods (#37)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-08-09T14:48:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "355e17769378ab17b69dac838ccad3e9cdd094e6",
          "body": null,
          "is_bot": false,
          "headline": "Fix (?) security permissions for GHA workflow. (#36)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-08-09T06:08:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5cebf9a184eeeacb70672a9717f595801c5583fe",
          "body": "…lisher set up) (#35)",
          "is_bot": false,
          "headline": "Remove token from publish-to-pypi (counterproductive with trusted pub…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-08-09T05:53:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18bbce2bc54ce74410338b37274374e70ad90cc7",
          "body": "…ased on `policy_input`s (#32)\n\n### What problem do you want to solve?\n\nCloses #27, #30, #24\n\nThis PR fixes the root problem of many issues we had with the\ncreation of templates and plotting: derived functions are part of the\n`specialized_environment` only if they can be build from input data or\noth\n[…]\n53113+pre-commit-ci[bot]@users.noreply.github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Build inputs template and plotting DAG from specialized environment b…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-08-09T05:32:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "57cc3536b3a4df5360d6d7eba9178769d32b2020",
          "body": "- Improve speed of pandas -> jax array conversion by going via numpy.\n- Improve duplicate detection algorithm.\n- Speed improvement is more than 12-fold in some cases.",
          "is_bot": false,
          "headline": "Optimize JAX performance in data preparation pipeline (#34)",
          "author_name": "JuergenWiemers",
          "author_login": "JuergenWiemers",
          "committed_at": "2025-08-08T15:08:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c69ccd08d0002b496029d5d9934ae61f442d1b77",
          "body": "### What problem do you want to solve?\n\nThe TT DAG has orphan nodes when not selecting a target via\n`NodeSelector`.\n\n- Those nodes are: `evaluation_x`, `policy_x` for `x \\in [\"year\",\n\"month\", \"day\", \"date\"]`\n- They are part of the DAG because they are `policy_inputs`\n- They are orphaned if they are \n[…]\nnction`s\n(in GETTSIM), not `policy_function`s.\n\nIf this is the case, this PR removes the explicitly from the plotting\nDAG.\n\n---------\n\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Remove orphaned nodes from tt dag (#23)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-08-01T06:58:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "23bfd12dd05b0b8d38195f6c207f777ade3afceb",
          "body": "- Move `mettsim` into its own folder and package, which mirrors the\nstructure of `gettsim` in that it has subfolders `middle_earth` and\n`tests_middle_earth`.\n- Use imported `middle_earth.ROOT_PATH` instead of `METTSIM_ROOT`s\ndefined in dozens of files.\n- [unrelated] In `./pyproject.toml`, use `hatch-vcs` instead of\n`hatch_vcs` since conda linter complained.",
          "is_bot": false,
          "headline": "Improve \"packaging\" of METTSIM (#22)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-28T08:43:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e5efdbb9950f46341c432eef1adb0c34d467d11d",
          "body": null,
          "is_bot": false,
          "headline": "Fix changelog. (#21)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-27T13:51:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a53ba806ceaa08956943586086eb44ca5943f321",
          "body": "Mostly motivated by a corresponding GETTSIM PR, this does the following:\n\n- Rename the `mettsim` / `mettsim_tests` directories to `middle_earth`\nand `tests_middle_earth`\n- Rename the `test_data` directory to `policy_cases` and adjust function\nnames.",
          "is_bot": false,
          "headline": "Clearer architecture (#19)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-27T13:37:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "87227c2cd1d00cb7eeda18163266576f9153399d",
          "body": "### What problem do you want to solve?\n\nAdds the param type `sparse_to_consecutive_int_lookup_table` alongside\nof `convert_sparse_to_consecutive_int_lookup_table`.\n\n---------\n\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Add type for sparse dict with integer keys param (#17)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-27T07:08:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ee64da3a99454ec087814678906217fbdbc6ae2",
          "body": "- As discussed in [GETTSIM\n#1055](https://github.com/ttsim-dev/gettsim/pull/1055), we sometimes\nwant to fail or issue a warning if a certain node is visited in the\ngraph. This PR adds a mechanism for that along with corresponding tests\n- Implementation: \n  - All `ColumnObjects` and `ParamFunctions` \n[…]\ned that `tt_dag` is a better name\nfor `tax_transfer_dag` as we are using that abbreviation much more\nextensively that 'tax_transfer' by now. Renamed, same for\n`tax_transfer_function` -> `tt_function`.",
          "is_bot": false,
          "headline": "Add fail/warn mechanism to ColumnObjects and ParamFunctions (#16)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-26T11:08:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4d049b1b3a97089c2aad00cb98165e4d0964bb28",
          "body": "Fix another GETTSIM test failure with Jax 0.7.0.\n\nAdd test and fix.",
          "is_bot": false,
          "headline": "Do not call len() on unsized arrays (#15)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-26T05:00:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cbf0e4c5c810a77618fcab90d9b480adc9335fef",
          "body": "…_changed' (#14)\n\nDoing so yields an error with Jax 0.7.0",
          "is_bot": false,
          "headline": "Do not loop over the attributes of Jax arrays in 'fail_if.backend_has…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-26T04:16:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6d9a098b45dbe96f90330c67b8b6587427ae62b8",
          "body": "### What problem do you want to solve?\n\nAs suggested\n[here](https://github.com/ttsim-dev/gettsim/pull/1056#pullrequestreview-3056621461).\n\n---------\n\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Put plotting functions in `ttsim.plot.dag` namespace (#13)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-26T03:57:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "58818e63bd807672c095bc8ec7661039f5fd89a4",
          "body": "### What problem do you want to solve?\n\nI tried both versions (with/without fail nodes) using GETTSIM's tests\nwith the py13 environment and backend numpy. 52 vs 53 seconds, so should\nbe no big deal turning it on.",
          "is_bot": false,
          "headline": "Turn on `fail_if` nodes when running policy tests (#12)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-25T19:47:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9fb394ccdc83a733b17d012f85d92c91831c4910",
          "body": "### What problem do you want to solve?\n\n@JuergenWiemers pointed out that some nodes are missing when calling\n`plot_tt_dag`.\n\nIt turned out that there were two bugs:\n1. When using `NodeSelector`, we didn't add the \"main\" node to the DAG\n(`nx` doesn't do so automatically)\n2. We still had an `include_o\n[…]\ntion of `evaluation_x`, `policy_x`, `xnp`,\n`dnp` and friends. This caused `policy_inputs` and some other nodes to\nbe thrown out of the DAG. I removed the argument completely, we don't\nneed it anymore.",
          "is_bot": false,
          "headline": "Fix several bugs in `plot_tt_dag` (#11)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-25T19:21:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "44a38d263aee7fd23bda0e694d4179b4eed35de9",
          "body": "### What problem do you want to solve?\n\n- Fix #4 \n- Had to change quite a few tests which should have failed for other\nreasons, but where this error came first now.\n- Update pixi on GHA\n\n---------\n\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "Raise error when passing data but no targets (#9)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-25T17:33:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e10fd6d189dfed950ac48862e7e1f1a7b7493dba",
          "body": "Closes #7",
          "is_bot": false,
          "headline": "Allow for input data as targets. (#8)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-25T09:33:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8889863fa2f8f66c7b518c2d9180f77bf3d2350d",
          "body": "Fail if the leaf name of an object in the policy environment differs from the last element of the path",
          "is_bot": false,
          "headline": "Merge pull request #6 from ttsim-dev/fail_if_leaf_name_diff",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-25T09:22:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ffa681ef47173c7a4d044974020f06a0fb7d3c35",
          "body": null,
          "is_bot": false,
          "headline": "Fix workflow file, exclude elements need to be atomic.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-25T07:57:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b313688d9c283d9033fa0620a33c1a95ad375a04",
          "body": null,
          "is_bot": false,
          "headline": "Remove leaf name attribute from ParamObjects.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-25T07:55:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "737f684ae962a27d4289126ba2aeca800d8117f6",
          "body": null,
          "is_bot": false,
          "headline": "Don't specify default value to make things easier to read; renamings.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-25T07:25:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "89ea7902c599487c8be28b0a7feb8916a02af203",
          "body": "…eaks that come from formatter).",
          "is_bot": false,
          "headline": "Simplify fail function, correct regex in test (never depend on linebr…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-25T07:24:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "432ad81bf1b26c41d06de48cfc0150a002e7b2b8",
          "body": null,
          "is_bot": false,
          "headline": "Improve error message a bit.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-24T20:20:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d88a15781ef2355db114c87a9b35b4dbf73869ab",
          "body": null,
          "is_bot": false,
          "headline": "Fail if leaf name is different than last path element.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-24T20:17:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2ebbfa1a3a2920e16d2cbee5203bc8c4d34de4b9",
          "body": "…me package to ttsim-backend because PyPI decided it was too similar to an existing package.",
          "is_bot": false,
          "headline": "Renamings as described in PR #3 (history was messed up), have to rena…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T14:03:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ceb05ca61935fbb1f225addb2e368ed91c6839bd",
          "body": null,
          "is_bot": false,
          "headline": "Initial standalone TTSIM",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T10:40:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "22292553e9f00cffeb39f0f1784ccb9796e42c8d",
          "body": null,
          "is_bot": false,
          "headline": "Add mac object to gitignore, wording in question template.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-24T10:24:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5a9302d6d4b5b98a9b1134fb29b5fe2bc054f4a3",
          "body": "… function.",
          "is_bot": false,
          "headline": "Allow not requiring correct type annotations in the taxes & transfers…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T09:20:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "67fae6b21d5d1b301e852702f096bf338061b017",
          "body": null,
          "is_bot": false,
          "headline": "Add comment to params schema file.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T09:03:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "21f01d5c60209969cf0843957c004adea42149d1",
          "body": null,
          "is_bot": false,
          "headline": "Fix versioning.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T08:24:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ec7006ddde69ef53aac56ecc78a9abd37fb173ba",
          "body": null,
          "is_bot": false,
          "headline": "Initial shot of removing GETTSIM.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-24T08:11:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ad1d40e347f11a2f15c45cd8a01aaea8b5577097",
          "body": null,
          "is_bot": false,
          "headline": "Remove explicitly adding group ID to template, see issue #1051.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-23T17:17:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5fe956fc8a8d1c5b9ae55a40d389ea105254b4b9",
          "body": "### What problem do you want to solve?\n\n- [x] Add a GEP for the revamped interface\n- [x] Update earlier GEPs to reflect the changes that have become\nnecessary after GEP 6 (since our documentation is small, it does not\nmake sense to keep outdated things around).\n- [x] Add the finalised schema from #8\n[…]\nppendix to GEP 3\n\n[Resolution on Zulip.](https://gettsim.zulipchat.com/#narrow/channel/309998-GEPs/topic/GEP.2007/near/530389224)\n\n---------\n\nCo-authored-by: Marvin Immesberger <immesberger@posteo.de>",
          "is_bot": false,
          "headline": "GEP 7 and updates to GEPs 1-5 necessitated by GEP 6 (#855)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-23T16:42:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "84daa3c28155261741f363d18d7292bd794f78ab",
          "body": "…omatically created function (#1050)\n\n### What problem do you want to solve?\n\nCloses #1049\n\n---------\n\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Allow input template creation when path to root node traverses an aut…",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-23T16:31:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b88e3e1615af3242dde3b45965b12f4461cf0e22",
          "body": "…994 (#1048)\n\nCheck whether the structure of the paths matches. E.g.:\n\n- `input_data={\"df_and_mapper\": None}`: Fails because there needs to be\na dict below \"df_and_mapper\"\n- `input_data={\"not_around\": None}`: Fails because `not_around` is not a\nvalid child of `input_data`\n- `not_around=None`: Fails because not around is not a valid root node\n(already taken care of by Python itself when calling `main`, but let's\nbe pedantic...)",
          "is_bot": false,
          "headline": "Raise an error if invalid input paths are provided to `main`, fixes #…",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-23T14:23:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "205f3585d90f780cd3794ce0cb813ba5d4d757e2",
          "body": "Add `fail_if.backend_has_changed`. \n\nLessons learned:\n- Numpy can handle Jax arrays (see test)\n- Jax can handle NumPy arrays that are passed as the processed data (see\ntest)\n- The problematic case are parameters that are partialled to functions.\nUnfortunately, these are typically custom objects. We to loop over them and\ncheck whether any of them happens to be a numpy array",
          "is_bot": false,
          "headline": "Fail if backend has changed (#1046)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-23T14:15:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3437b127aee77aba704f97879c3bfa5729db279c",
          "body": "Fixes #869\n\n- Raise an error when type-conversion calls like float, int, etc. are encountered\n- Raise an error when augmented assignment (+= and -= and *= and /= and friends) are encountered\n- Remove some tests that actually relied on that behavior, make test numbering consistent.",
          "is_bot": false,
          "headline": "Raise more errors during vectorization (#1043)",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-23T09:37:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e4e7ada6c81c8829d2aeef07838b2cf5b6b2da63",
          "body": "### What problem do you want to solve?\n\nCloses #999 \n\nFails if any `param_function` depends on a `ColumnObject`, with the\nexception being `evaluation_x` and `policy_x` (they are `PolicyInputs`).\n\n---------\n\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Fail if ParamFunctions depends on ColumnObjects (#1044)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-23T08:12:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "30662fa06b8e08612c3a55f2cd68a20de4863567",
          "body": "This reverts commit 6f12a598df1a216d123617b94a9cbb534499f20f.",
          "is_bot": false,
          "headline": "Revert \"Fail if param functions depend on policy functions.\"",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-22T15:45:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6f12a598df1a216d123617b94a9cbb534499f20f",
          "body": null,
          "is_bot": false,
          "headline": "Fail if param functions depend on policy functions.",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-22T15:44:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cbf95c1fddc77955384e41fcdf62007cb256f9f8",
          "body": null,
          "is_bot": false,
          "headline": "Had merged without double-checking all tests...",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-22T13:26:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3f3532215a3d3d4827e79bf68e7680a6a42294f1",
          "body": "*Leaving the almost-unedited stuff from Claude Code here for\ndemonstration purposes*\n\n## Summary\n\n- Implements `copy_environment` function to address issue #1008\n- Provides proper copying of policy environments containing unpickleable\nfunction objects\n- Available in both `ttsim` and `gettsim` namesp\n[…]\n--\n\nCo-authored-by: Marvin Immesberger <74215010+MImmesberger@users.noreply.github.com>\nCo-authored-by: Claude <noreply@anthropic.com>\nCo-authored-by: Hans-Martin von Gaudecker <hmgaudecker@gmail.com>",
          "is_bot": false,
          "headline": "Add copy_environment function (#1008) (#1042)",
          "author_name": "Tim Mensinger",
          "author_login": "timmens",
          "committed_at": "2025-07-22T13:01:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6e4f869a52532b35a7745d5d2f14ffdbdf469f0e",
          "body": "### What problem do you want to solve?\n\nCloses #757\n\nIn fact, any occurences of automatically created cycles reported in #757\nhave already been solved via the namespaces structure.\n\nIn ALG2 we have\n\n```python\n@policy_function(start_date=\"2005-01-01\")\ndef wohnfläche(\n    wohnen__wohnfläche_hh: float,\n[…]\nrbeitslosengeld_2__wohnfläche`.\n\nStill, thanks to the work done by Lars in the past, a general TTSIM\nsolution was easy to implement because it just copies the logic done for\ntime-conversion functions.",
          "is_bot": false,
          "headline": "Don't create DAG cycles via automatically added functions (#1041)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-22T12:46:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c4946933cb4073e73d33d47884fcc04a2e1e060e",
          "body": null,
          "is_bot": false,
          "headline": "Fix links, formatting, add 1041.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-22T10:32:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "395a7875311971d498491d5584c6907efa551f21",
          "body": null,
          "is_bot": false,
          "headline": "Fix links.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-22T09:58:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3ef80ca67f125e29adb1d963eb0dc0025fdcd995",
          "body": null,
          "is_bot": false,
          "headline": "Update changelog.",
          "author_name": "Hans-Martin von Gaudecker",
          "author_login": "hmgaudecker",
          "committed_at": "2025-07-22T09:52:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "43279825e47409637e75a6b2ccc9f7214b9dc25f",
          "body": "### What problem do you want to solve?\n\nThis PR gets the input names (+docstrings) of \"vorjahr\" and similar inputs straight\nand puts them in the correct namespaces.",
          "is_bot": false,
          "headline": "Vorjahr input names and their namespaces (#1040)",
          "author_name": "Marvin Immesberger",
          "author_login": "MImmesberger",
          "committed_at": "2025-07-22T09:51:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 10,
      "commits_last_year": 62,
      "latest_release_at": "2026-05-25T11:01:22Z",
      "latest_release_tag": "v1.2.1",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 22,
      "days_since_latest_release": 62,
      "mean_days_between_releases": 33.9
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 75,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "ttsim-backend",
          "exists": true,
          "license": null,
          "keywords": [
            "Benefits",
            "Economics",
            "Pensions",
            "Taxes",
            "Transfers",
            "Intended Audience :: Science/Research",
            "License :: OSI Approved :: GNU Affero General Public License v3",
            "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
            "Operating System :: MacOS :: MacOS X",
            "Operating System :: Microsoft :: Windows",
            "Operating System :: POSIX",
            "Operating System :: Unix",
            "Programming Language :: Python :: 3 :: Only",
            "Programming Language :: Python :: 3.11",
            "Programming Language :: Python :: 3.12",
            "Programming Language :: Python :: 3.13",
            "Programming Language :: Python :: 3.14"
          ],
          "ecosystem": "pypi",
          "matches_repo": true,
          "registry_url": "https://pypi.org/project/ttsim-backend/",
          "is_deprecated": false,
          "latest_version": "1.2.1",
          "repository_url": "https://github.com/ttsim-dev/ttsim",
          "versions_count": 10,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": "2025-07-24T13:53:24.377332Z",
          "latest_published_at": "2026-05-25T11:01:50.813290Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 62
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 2,
      "watchers": 2,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 12
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "notebooks"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 61629,
      "source_files_sampled": 115,
      "oversized_source_files": 1,
      "agent_instruction_files": [
        "AGENTS.md",
        "CLAUDE.md",
        "GEMINI.md"
      ],
      "agent_instruction_max_bytes": 6112
    },
    "dependencies": {
      "manifests": [
        "pyproject.toml",
        "src_mettsim/pyproject.toml"
      ],
      "advisories": {
        "error": null,
        "scope": "published_package",
        "source": "osv",
        "findings": [],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 49,
        "malicious_count": 0,
        "assessed_package": "pypi:ttsim-backend@1.2.1",
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "beartype",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.18"
        },
        {
          "name": "dags",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.6"
        },
        {
          "name": "ipywidgets",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "jaxtyping",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.3.10"
        },
        {
          "name": "kaleido",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.1"
        },
        {
          "name": "networkx",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3.5"
        },
        {
          "name": "numpy",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "numpy-groupies",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "openpyxl",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "optree",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.16"
        },
        {
          "name": "pandas",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3"
        },
        {
          "name": "plotly",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=6.5"
        },
        {
          "name": "portion",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "pygments",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "pygraphviz",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "pytest",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        },
        {
          "name": "pyyaml",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": null
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 4,
        "merged_prs": 81,
        "open_issues": 8,
        "closed_ratio": 0.778,
        "closed_issues": 28,
        "closed_unmerged_prs": 25
      },
      "bus_factor": 3,
      "bot_contributors": 2,
      "top_contributors": [
        {
          "type": "User",
          "login": "hmgaudecker",
          "commits": 155,
          "avatar_url": "https://avatars.githubusercontent.com/u/158456?v=4"
        },
        {
          "type": "User",
          "login": "MaxBlesch",
          "commits": 106,
          "avatar_url": "https://avatars.githubusercontent.com/u/33116902?v=4"
        },
        {
          "type": "User",
          "login": "MImmesberger",
          "commits": 97,
          "avatar_url": "https://avatars.githubusercontent.com/u/74215010?v=4"
        },
        {
          "type": "User",
          "login": "Eric-Sommer",
          "commits": 67,
          "avatar_url": "https://avatars.githubusercontent.com/u/44068467?v=4"
        },
        {
          "type": "User",
          "login": "tobiasraabe",
          "commits": 42,
          "avatar_url": "https://avatars.githubusercontent.com/u/22533006?v=4"
        },
        {
          "type": "User",
          "login": "LauraGergeleit",
          "commits": 23,
          "avatar_url": "https://avatars.githubusercontent.com/u/93579663?v=4"
        },
        {
          "type": "User",
          "login": "janosg",
          "commits": 22,
          "avatar_url": "https://avatars.githubusercontent.com/u/9536380?v=4"
        },
        {
          "type": "User",
          "login": "mjbloemer",
          "commits": 21,
          "avatar_url": "https://avatars.githubusercontent.com/u/31305679?v=4"
        },
        {
          "type": "User",
          "login": "lars-reimann",
          "commits": 20,
          "avatar_url": "https://avatars.githubusercontent.com/u/2501322?v=4"
        },
        {
          "type": "User",
          "login": "JuergenWiemers",
          "commits": 9,
          "avatar_url": "https://avatars.githubusercontent.com/u/22220065?v=4"
        }
      ],
      "contributors_sampled": 28,
      "top_contributor_share": 0.255
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "main.yml",
        "publish-to-pypi.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": true
    },
    "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": "28 out of 28 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": 9,
            "reason": "Found 27/30 approved changesets -- score normalized to 9",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 10,
            "reason": "project has 16 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": 10,
            "reason": "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": "18 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": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "0fdc84b1753d52b3a35015a966dcb250d98c39aa",
        "ran_at": "2026-07-26T19:58:00Z",
        "aggregate_score": 6.6,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": true
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-01T00:43:30Z",
      "oldest_open_prs": [
        {
          "number": 138,
          "created_at": "2026-06-27T12:24:58Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 141,
          "created_at": "2026-06-27T12:25:05Z",
          "last_comment_at": "2026-06-27T12:28:49Z",
          "last_comment_author": "codecov"
        },
        {
          "number": 142,
          "created_at": "2026-07-01T00:43:28Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 143,
          "created_at": "2026-07-06T17:05:25Z",
          "last_comment_at": "2026-07-06T17:07:52Z",
          "last_comment_author": "codecov"
        }
      ],
      "last_merged_pr_at": "2026-07-17T12:50:38Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 29,
          "created_at": "2025-07-23T09:47:16Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 117,
          "created_at": "2026-06-03T15:52:34Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 118,
          "created_at": "2026-06-03T15:52:35Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 119,
          "created_at": "2026-06-03T15:52:37Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 120,
          "created_at": "2026-06-03T15:52:39Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 121,
          "created_at": "2026-06-03T15:52:40Z",
          "last_comment_at": "2026-06-03T20:22:45Z",
          "last_comment_author": "MImmesberger"
        },
        {
          "number": 128,
          "created_at": "2026-06-06T12:26:21Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 134,
          "created_at": "2026-06-16T19:52:25Z",
          "last_comment_at": "2026-06-16T20:33:19Z",
          "last_comment_author": "MImmesberger"
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/ttsim-dev/ttsim",
    "host": "github.com",
    "name": "ttsim",
    "owner": "ttsim-dev"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 70,
      "inputs": {
        "security": 73,
        "vitality": 86,
        "community": 34,
        "governance": 74,
        "engineering": 78
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 86,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 77,
            "inputs": {
              "commits_last_year": 62,
              "human_commit_share": 0.97,
              "days_since_last_push": 0,
              "active_weeks_last_year": 22
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "22/52 weeks with commits",
                "points": 15.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 22
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "62 commits in the last year",
                "points": 16.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 62
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "18 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": "excellent",
            "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": 100,
            "inputs": {
              "releases_count": 10,
              "latest_release_tag": "v1.2.1",
              "releases_from_tags": false,
              "days_since_latest_release": 62,
              "mean_days_between_releases": 33.9
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "10 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 10
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 62 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 62
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~33.9 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 33.9
                    }
                  }
                ],
                "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": 32,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 32 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 32
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "at_risk",
        "name": "Community & Adoption",
        "value": 34,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 2,
              "watchers": 2,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "2 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "2 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (AGPL-3.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "AGPL-3.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "good",
        "name": "Sustainability & Governance",
        "value": 74,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "good",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 76,
            "inputs": {
              "bus_factor": 3,
              "contributors_sampled": 28,
              "top_contributor_share": 0.255
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "3 contributor(s) cover half of all commits",
                "points": 36,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 26% of commits",
                "points": 16.8,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 26
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "28 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 28
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 16 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 79,
            "inputs": {
              "merged_prs": 81,
              "open_issues": 8,
              "closed_issues": 28,
              "issue_closed_ratio": 0.778,
              "closed_unmerged_prs": 25
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "78% of issues closed",
                "points": 36.4,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 78
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "81/106 decided PRs merged",
                "points": 29.2,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 81,
                      "decided": 106
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 27/30 approved changesets -- score normalized to 9",
                "points": 13.5,
                "status": "partial",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 46,
            "inputs": {
              "followers": 6,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "ttsim-dev",
              "public_repos": 7,
              "account_age_days": 516
            },
            "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": "6 followers of ttsim-dev",
                "points": 6.1,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 6,
                      "login": "ttsim-dev"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "7 public repos, account ~1 yr old",
                "points": 9.4,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 7
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 1
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "ttsim-backend"
              ],
              "ecosystems": "pypi",
              "any_deprecated": false,
              "min_days_since_publish": 62
            },
            "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 62 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 62
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "10 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 10
                    }
                  }
                ],
                "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": 78,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 94,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": true
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "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": 16,
                "status": "met",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 9.6,
                "status": "met",
                "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": "28 out of 28 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "topics": [],
              "has_wiki": false,
              "homepage": "https://gettsim.readthedocs.io/",
              "has_readme": true,
              "has_docs_dir": false,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://gettsim.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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "good",
        "name": "Security",
        "value": 73,
        "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": 66,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 6.6
            },
            "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": "28 out of 28 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 27/30 approved changesets -- score normalized to 9",
                "points": 6.8,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 16 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": "update tool detected",
                "points": 7.5,
                "status": "met",
                "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": "18 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": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "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:ttsim-backend@1.2.1 runtime dependency closure — what installing the published package pulls in — 49 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:ttsim-backend@1.2.1",
                  "assessed": 49
                }
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 0,
              "affected_packages": 0,
              "assessed_packages": 49,
              "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": 49,
              "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": 12
            },
            "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": 60,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.763,
              "agent_instruction_files": [
                "AGENTS.md",
                "CLAUDE.md",
                "GEMINI.md"
              ],
              "agent_instruction_max_bytes": 6112
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "AGENTS.md, CLAUDE.md, GEMINI.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "AGENTS.md, CLAUDE.md, GEMINI.md"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "74 of 97 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 74,
                      "sampled": 97
                    }
                  }
                ],
                "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": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0.23,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0.02
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": 11,
                "status": "met",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "23 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 23,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "2 of the last 100 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 2,
                      "sampled": 100
                    }
                  }
                ],
                "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": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 54,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 61629,
              "source_files_sampled": 115,
              "oversized_source_files": 1
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "1/115 source files over 60KB",
                "points": 54.5,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 115,
                      "oversized": 1
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "notebooks"
              ],
              "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": "notebooks",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "notebooks"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "Could not fetch pypi package 'mettsim' from its registry",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-26T19:58:27.877413Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/t/ttsim-dev/ttsim.svg",
  "full_name": "ttsim-dev/ttsim",
  "license_state": "standard",
  "license_spdx": "AGPL-3.0"
}

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.