Public record
Software health reportschema 0.27.0 · metrics 1.13.0 · 2026-07-26 19:58 UTC

ttsim-dev / ttsim

The computing engine powering GETTSIM

PythonAGPL-3.0★ 2 stars⑂ 0 forkssince Jul 2025View on GitHub ↗

ttsim-dev/ttsim holds a health index of 70 out of 100, placing it in the Good band. It scores highest on Vitality (86/100) and lowest on Community & Adoption (34/100). It was last updated today. 3 contributors account for most of its recent work.

70
overall / 100
Good

Software health index

Metrics are grouped into weighted categories on one standardized 1–100 scale. Overall starts as their weighted mean; when public evidence triggers the High-Risk Jurisdiction Policy, the rating is adjusted and receives an At risk ceiling of 49. AI Readiness sits outside the overall score.

70
Excellent85-100Exemplary; meets essentially all checked criteria
Good70-84Healthy; minor gaps
Moderate50-69Acceptable with notable gaps; review recommended
At risk30-49Significant weaknesses; adoption warrants caution
Critical1-29Severe problems (abandoned, single-maintainer, no hygiene)
VitalityCommunity &AdoptionSustainability &GovernanceEngineeringQualitySecurityAI Readiness

Score profile

Each axis is a category. The shape matters more than the average — a healthy subject fills the whole shape, while a spike-and-crater profile means strength in one dimension is masking risk in another.

Ownership

6 followers7 public repossince Feb 2025

This repository is backed by an organization — shared, accountable stewardship that can outlive any single maintainer.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publishTags
PyPIttsim-backend1.2.1-1062 days agobenefitseconomicspensionstaxestransfers

Metrics by category

Vitality

Is the project alive — is code being written and are releases shipping?

86Excellent · 22% of overall
How it's scored
36/36Push recency — last push 0 days ago
15.2/36Commit cadence — 22/52 weeks with commits
16.2/18Commit volume — 62 commits in the last year
10/10OpenSSF Scorecard: Maintained — 18 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year62
human_commit_share0.97
days_since_last_push0
active_weeks_last_year22

Release discipline

100Excellent
How it's scored
27/27Ships releases — 10 releases published
36/36Release recency — latest release 62 days ago
27/27Release cadence — a release every ~33.9 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count10
latest_release_tagv1.2.1
releases_from_tagsno
days_since_latest_release62
mean_days_between_releases33.9
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

Does the project have users, downloads, attention, and a welcoming setup for contributors?

34At risk · 18% of overall
How it's scored
0/60Stars — 2 stars
0/25Forks — 0 forks
0/15Watchers — 2 watchers
Inputs used
forks0
stars2
watchers2
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (AGPL-3.0)
0/18CONTRIBUTING guide
13.5/13.5Code of conduct
0/7.2Issue template
6.3/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingno
has_issue_templateno
has_code_of_conductyes
has_pull_request_templateyes

Sustainability & Governance

Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?

74Good · 24% of overall
How it's scored
36/54Bus factor — 3 contributor(s) cover half of all commits
16.8/22.5Commit distribution — top contributor authored 26% of commits
13.5/13.5Contributor breadth — 28 contributors
10/10OpenSSF Scorecard: Contributors — project has 16 contributing companies or organizations
Inputs used
bus_factor3
contributors_sampled28
top_contributor_share0.255
How it's scored
36.4/46.8Issue resolution — 78% of issues closed
29.2/38.3PR acceptance — 81/106 decided PRs merged
13.5/15OpenSSF Scorecard: Code-Review — Found 27/30 approved changesets -- score normalized to 9
Inputs used
merged_prs81
open_issues8
closed_issues28
issue_closed_ratio0.778
closed_unmerged_prs25
How it's scored
30/30Ownership backing — organization-owned
0/20Verified domain
6.1/25Owner reach — 6 followers of ttsim-dev
9.4/25Track record — 7 public repos, account ~1 yr old
Inputs used
followers6
owner_typeOrganization
is_verified
owner_loginttsim-dev
public_repos7
account_age_days516
How it's scored
25/25Published & resolvable — 1 package(s) on pypi
35/35Publish recency — latest publish 62 days ago
20/20Version history — 10 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagesttsim-backend
ecosystemspypi
any_deprecatedno
min_days_since_publish62

Engineering Quality

Are baseline engineering and documentation practices in place?

78Good · 20% of overall
How it's scored
24/24CI workflows — 2 workflow(s)
24/24Tests present
16/16Linter config
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
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configyes

Documentation

55Moderate
How it's scored
30/30README
0/25Documentation directory
15/15Documentation / homepage site — https://gettsim.readthedocs.io/
10/10Repository description
0/10Topics
0/10Wiki
Inputs used
topics
has_wikino
homepagehttps://gettsim.readthedocs.io/
has_readmeyes
has_docs_dirno
has_descriptionyes

Security

Are visible security and supply-chain practices strong, without unresolved high-risk jurisdiction exposure?

73Good · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — no data
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.5License — 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 — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate6.6
Excluded from scoring (no data or not applicable): branch_protection, signed_releases. Remaining weights renormalized.
How it's scored
35/35Direct dependencies free of known advisories — no direct dependency carries a known advisory
25/25Indirect dependencies free of known advisories — no indirect dependency carries a known advisory
0/40No advisories left outstanding — no advisory carries a publication date
Inputs used
sourceosv
advisories0
affected_packages0
assessed_packages49
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
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.

AI Readiness

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.

60Moderate · 0% of overall
How it's scored
45/45Agent instructions — AGENTS.md, CLAUDE.md, GEMINI.md
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 74 of 97 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.763
agent_instruction_filesAGENTS.md, CLAUDE.md, GEMINI.md
agent_instruction_max_bytes6,112
How it's scored
0/18One-command bootstrap
22/22Automated tests
11/11Lint / format config
0/11Static type checking
0/10Reproducible environment
10/10Demonstrated agent practice — 23 of the last 100 commits agent-authored or agent-credited
8/8Automated maintenance — 2 of the last 100 commits are automated dependency updates
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfiles
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configyes
typecheck_configs
agent_commit_share0.23
toolchain_manifests
dependency_bot_commit_share0.02
How it's scored
0/45Type-checkable code — Python without a type-check config
54.5/55Manageable file sizes — 1/115 source files over 60KB
Inputs used
primary_languagePython
largest_source_bytes61,629
source_files_sampled115
oversized_source_files1
How it's scored
0/40API schema (OpenAPI/GraphQL/proto)
0/20MCP server
40/40Runnable examples — notebooks
Inputs used
example_dirsnotebooks
has_mcp_signalno
api_schema_files

Key facts

2GitHub stars
28contributors
62commits, last 12 months
0days since last push
10releases
3bus factor
8open issues
PyPIpackage ecosystems

Data collection 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

More detail

OpenSSF Scorecard 6.6 / 10
6.6aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).Scorecard v5.5.0 · 2026-07-26 19:58 UTC

10Binary-Artifactsno binaries found in the repo
n/aBranch-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
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Direct dependencies 17
RegistryPackageVersion constraintManifest
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
All dependencies not collected

The resolved dependency set could not be collected for this report: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Dependency advisories 0

Installing pypi:ttsim-backend@1.2.1 pulls in 49 packages, direct and transitive: 0 carry known advisories, of which 0 are direct dependencies.

No known advisories affect the assessed dependencies.

An advisory means the version recorded in the dependency graph falls inside an advisory’s affected range. Reachability is not analysed, and the graph includes development and test pins — a finding may concern tooling rather than shipped software.

Raw JSON report machine-readable
{
  "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"
}

Scores are signals, not warranties. They reflect publicly visible practices on GitHub — not a code audit, and not a security guarantee.

Missing data is excluded and weights renormalized, never scored as zero. Methodology is versioned and open: metrics v1.13.0, schema v0.27.0 — full methodology · metrics wiki.

How one result sits in the wider record: aggregate statisticsPyPI.