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

openrewrite / rewrite-testing-frameworks

OpenRewrite recipes that perform common Java testing migration tasks.

JavaLicencia propia★ 99 estrellas⑂ 104 forksdesde sept 2020Ver en GitHub ↗

openrewrite/rewrite-testing-frameworks tiene un índice de salud de 72 sobre 100, lo que lo sitúa en la banda Bueno. Su puntuación más alta es Vitality (100/100) y la más baja, Security (52/100). Se actualizó por última vez hoy. 3 personas concentran la mayor parte del trabajo reciente.

72
global / 100
Bueno

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala de 1 a 100. El resultado global parte de su media; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe el límite 49 (En riesgo). Preparación para IA queda fuera.

72
Excelente85-100Ejemplar; cumple prácticamente todos los criterios evaluados
Bueno70-84Saludable; carencias menores
Moderado50-69Aceptable con carencias notables; se recomienda revisión
En riesgo30-49Debilidades significativas; su adopción exige cautela
Crítico1-29Problemas graves (proyecto abandonado, un solo mantenedor, sin higiene)
VitalidadComunidad yAdopciónSostenibilidady GobernanzaCalidad deIngenieríaSeguridadPreparaciónpara IA

Perfil de puntuación

Cada eje es una categoría. La forma importa más que la media: un proyecto sano llena toda la figura, mientras que un perfil de picos y cráteres indica que la fortaleza en una dimensión enmascara el riesgo en otra.

Titularidad

OpenRewriteOrganización
984 seguidores75 repositorios públicosdesde feb 2020

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

Métricas por categoría

Vitalidad

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

100Excelente · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
36/36Cadencia de commits — 52/52 semanas con commits
18/18Volumen de commits — 314 commits en el último año
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Datos de entrada utilizados
commits_last_year314
human_commit_share1
days_since_last_push0
active_weeks_last_year52
Cómo se puntúa
27/27Publica versiones — 100 versiones publicadas
36/36Recencia de las versiones — última versión hace 5 días
27/27Cadencia de publicación — una versión cada ~9,4 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count100
latest_release_tagv3.42.1
releases_from_tagsno
days_since_latest_release5
mean_days_between_releases9,4
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

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

61Moderado · 18% del índice global
Cómo se puntúa
32.3/60Estrellas — 99 estrellas
16.8/25Forks — 104 forks
4.3/15Observadores — 7 observadores
Datos de entrada utilizados
forks104
stars99
watchers7
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
16.9/22.5Licencia — archivo de licencia presente, no es una licencia reconocida
18/18Guía CONTRIBUTING
0/13.5Código de conducta
0/7.2Plantilla de issues
6.3/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributing
has_issue_templateno
has_code_of_conductno
has_pull_request_template

Sostenibilidad y Gobernanza

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

77Bueno · 24% del índice global
Cómo se puntúa
36/54Factor bus — la mitad de los commits recae en 3 contribuyente(s)
15.3/22.5Distribución de commits — el principal contribuyente firma el 32% de los commits
13.5/13.5Amplitud de contribuyentes — 98 contribuyentes
10/10OpenSSF Scorecard: Contributors — project has 16 contributing companies or organizations
Datos de entrada utilizados
bus_factor3
contributors_sampled98
top_contributor_share0,318
Cómo se puntúa
43.9/46.8Resolución de issues — 94% de issues cerradas
34.4/38.3Aceptación de PR — 603/671 PR decididos fusionados
3/15OpenSSF Scorecard: Code-Review — Found 8/30 approved changesets -- score normalized to 2
Datos de entrada utilizados
merged_prs603
open_issues23
closed_issues361
issue_closed_ratio0,94
closed_unmerged_prs68
Cómo se puntúa
30/30Respaldo de la propiedad — propiedad de una organización
0/20Dominio verificado
21.5/25Alcance del propietario — 984 seguidores de openrewrite
25/25Trayectoria — 75 repos públicos, cuenta de ~6 años
Datos de entrada utilizados
followers984
owner_typeOrganization
is_verified
owner_loginopenrewrite
public_repos75
account_age_days2341

Calidad de Ingeniería

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

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

Documentación

50Moderado
Cómo se puntúa
30/30README
0/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
10/10Topics — 2 topics
0/10Wiki
Datos de entrada utilizados
topicsopenrewrite, openrewrite-recipes
has_wikino
homepage
has_readme
has_docs_dirno
has_description

Seguridad

¿Son sólidas las prácticas visibles de seguridad y de cadena de suministro, sin exposición jurisdiccional de alto riesgo sin resolver?

52Moderado · 16% del índice global
Cómo se puntúa
6.8/7.5Binary-Artifacts — binaries present in source code
0/7.5Branch-Protection — branch protection not enabled on development/release branches
2.2/2.5CI-Tests — 22 out of 23 merged PRs checked by a CI test -- score normalized to 9
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
1.5/7.5Code-Review — Found 8/30 approved changesets -- score normalized to 2
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.2/2.5Licencia — license file detected
7.5/7.5Maintained — 30 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — sin datos
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate5,2
Excluidos de la puntuación (sin datos o no aplicable): packaging, signed_releases. Los pesos restantes se han renormalizado.

Preparación para IA

¿Hasta qué punto está el repositorio preparado para desarrollarse y mantenerse con agentes de codificación de IA? Es una insignia independiente y experimental — peso 0,0, de modo que se presenta por separado y no afecta a la puntuación de salud global.

58Moderado · 0% del índice global
Cómo se puntúa
0/45Instrucciones para agentes — sin CLAUDE.md / AGENTS.md / reglas de editor
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 90 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,9
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
12.6/18Arranque con un solo comando — build.gradle.kts (convención del toolchain, sin ejecutor de tareas)
22/22Pruebas automatizadas
0/11Configuración de lint / formato
11/11Verificación estática de tipos — Java (tipado estático)
0/10Entorno reproducible
4/10Práctica demostrada con agentes — 2 de los últimos 100 commits con autoría o crédito de agente
5/8Mantenimiento automatizado — automatización de dependencias configurada, no observada en los commits muestreados
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_language
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configs
agent_commit_share0,02
toolchain_manifestsbuild.gradle.kts
dependency_bot_commit_share0
Cómo se puntúa
45/45Código verificable por tipos — Java (tipado estático)
54.6/55Tamaños de archivo manejables — 3/402 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languageJava
largest_source_bytes73.880
source_files_sampled402
oversized_source_files3

Datos clave

99estrellas de GitHub
98contribuidores
314commits en los últimos 12 meses
0días desde el último push
100versiones publicadas
3factor bus
23issues abiertas
Mavenecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Más detalle

Historial de estrellas y forks 0 ★ / 104 ⇿
0Estrellas
104Forks
100Versiones

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

02040608010012010242020-122023-092026-07
Mayor 1Menor 66Parche 33

Cada punto abarca 6 días.

OpenSSF Scorecard 5.2 / 10
5.2agregado

Evaluación de seguridad independiente y agnóstica en cuanto a herramientas, procedente del proyecto de código abierto OpenSSF Scorecard. Cada comprobación premia una práctica de seguridad, no la herramienta de un proveedor concreto. Las comprobaciones que Scorecard no pudo determinar se marcan como n/d y se excluyen de la puntuación de seguridad (nunca se cuentan como cero).Scorecard v5.5.0 · 2026-07-25 17:21 UTC

9Binary-Artifactsbinaries present in source code
0Branch-Protectionbranch protection not enabled on development/release branches
9CI-Tests22 out of 23 merged PRs checked by a CI test -- score normalized to 9
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
2Code-ReviewFound 8/30 approved changesets -- score normalized to 2
10Contributorsproject has 16 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
9Licenselicense file detected
10Maintained30 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
n/dPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
n/dSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Todas las dependencias no recopilado

No fue posible recopilar el conjunto de dependencias resuelto para este informe: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [
        "openrewrite",
        "openrewrite-recipes"
      ],
      "is_fork": false,
      "size_kb": 64085,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Java": 3451857
      },
      "pushed_at": "2026-07-25T07:19:01Z",
      "created_at": "2020-09-15T05:07:44Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-25T07:19:04Z",
      "description": "OpenRewrite recipes that perform common Java testing migration tasks.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": "NOASSERTION",
      "primary_language": "Java",
      "significant_languages": [
        "Java"
      ]
    },
    "owner": {
      "blog": "https://docs.openrewrite.org",
      "name": "OpenRewrite",
      "type": "Organization",
      "login": "openrewrite",
      "company": null,
      "location": "San Francisco, CA",
      "followers": 984,
      "avatar_url": "https://avatars.githubusercontent.com/u/61478321?v=4",
      "created_at": "2020-02-25T21:27:36Z",
      "is_verified": null,
      "public_repos": 75,
      "account_age_days": 2341
    },
    "license": {
      "state": "custom",
      "spdx_id": null,
      "raw_spdx": "NOASSERTION",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v3.42.1",
          "kind": "patch",
          "published_at": "2026-07-20T14:04:34Z"
        },
        {
          "tag": "v3.42.0",
          "kind": "minor",
          "published_at": "2026-07-14T05:53:38Z"
        },
        {
          "tag": "v3.41.0",
          "kind": "minor",
          "published_at": "2026-07-01T06:17:41Z"
        },
        {
          "tag": "v3.40.0",
          "kind": "minor",
          "published_at": "2026-06-21T20:07:33Z"
        },
        {
          "tag": "v3.39.0",
          "kind": "minor",
          "published_at": "2026-06-19T13:58:30Z"
        },
        {
          "tag": "v3.38.0",
          "kind": "minor",
          "published_at": "2026-06-17T10:30:51Z"
        },
        {
          "tag": "v3.37.0",
          "kind": "minor",
          "published_at": "2026-06-03T14:24:35Z"
        },
        {
          "tag": "v3.36.0",
          "kind": "minor",
          "published_at": "2026-05-21T10:54:20Z"
        },
        {
          "tag": "v3.35.3",
          "kind": "patch",
          "published_at": "2026-05-07T07:24:44Z"
        },
        {
          "tag": "v3.35.2",
          "kind": "patch",
          "published_at": "2026-04-27T10:20:02Z"
        },
        {
          "tag": "v3.35.1",
          "kind": "patch",
          "published_at": "2026-04-23T11:10:22Z"
        },
        {
          "tag": "v3.35.0",
          "kind": "minor",
          "published_at": "2026-04-21T09:03:59Z"
        },
        {
          "tag": "v3.34.0",
          "kind": "minor",
          "published_at": "2026-04-08T13:23:21Z"
        },
        {
          "tag": "v3.33.0",
          "kind": "minor",
          "published_at": "2026-03-31T16:38:23Z"
        },
        {
          "tag": "v3.32.0",
          "kind": "minor",
          "published_at": "2026-03-27T16:16:12Z"
        },
        {
          "tag": "v3.31.0",
          "kind": "minor",
          "published_at": "2026-03-25T10:34:58Z"
        },
        {
          "tag": "v3.30.0",
          "kind": "minor",
          "published_at": "2026-03-12T14:44:02Z"
        },
        {
          "tag": "v3.29.0",
          "kind": "minor",
          "published_at": "2026-03-04T17:41:47Z"
        },
        {
          "tag": "v3.28.0",
          "kind": "minor",
          "published_at": "2026-02-25T13:13:05Z"
        },
        {
          "tag": "v3.27.0",
          "kind": "minor",
          "published_at": "2026-02-11T09:32:33Z"
        },
        {
          "tag": "v3.26.0",
          "kind": "minor",
          "published_at": "2026-01-20T19:51:12Z"
        },
        {
          "tag": "v3.25.0",
          "kind": "minor",
          "published_at": "2026-01-07T16:38:01Z"
        },
        {
          "tag": "v3.24.0",
          "kind": "minor",
          "published_at": "2025-12-16T16:59:41Z"
        },
        {
          "tag": "v3.23.0",
          "kind": "minor",
          "published_at": "2025-12-05T11:20:11Z"
        },
        {
          "tag": "v3.22.0",
          "kind": "minor",
          "published_at": "2025-11-20T12:20:32Z"
        },
        {
          "tag": "v3.21.3",
          "kind": "patch",
          "published_at": "2025-11-12T18:53:27Z"
        },
        {
          "tag": "v3.21.2",
          "kind": "patch",
          "published_at": "2025-11-05T14:39:14Z"
        },
        {
          "tag": "v3.21.1",
          "kind": "patch",
          "published_at": "2025-11-05T13:41:45Z"
        },
        {
          "tag": "v3.21.0",
          "kind": "minor",
          "published_at": "2025-11-04T15:13:50Z"
        },
        {
          "tag": "v3.20.0",
          "kind": "minor",
          "published_at": "2025-10-22T12:22:10Z"
        },
        {
          "tag": "v3.19.0",
          "kind": "minor",
          "published_at": "2025-10-10T17:54:11Z"
        },
        {
          "tag": "v3.18.0",
          "kind": "minor",
          "published_at": "2025-10-02T16:09:07Z"
        },
        {
          "tag": "v3.17.0",
          "kind": "minor",
          "published_at": "2025-09-24T13:03:02Z"
        },
        {
          "tag": "v3.16.3",
          "kind": "patch",
          "published_at": "2025-09-18T09:34:29Z"
        },
        {
          "tag": "v3.16.2",
          "kind": "patch",
          "published_at": "2025-09-10T11:24:08Z"
        },
        {
          "tag": "v3.16.1",
          "kind": "patch",
          "published_at": "2025-09-09T14:04:30Z"
        },
        {
          "tag": "v3.16.0",
          "kind": "minor",
          "published_at": "2025-08-27T16:34:56Z"
        },
        {
          "tag": "v3.15.1",
          "kind": "patch",
          "published_at": "2025-08-21T20:15:49Z"
        },
        {
          "tag": "v3.15.0",
          "kind": "minor",
          "published_at": "2025-08-12T21:32:47Z"
        },
        {
          "tag": "v3.14.1",
          "kind": "patch",
          "published_at": "2025-07-23T21:58:51Z"
        },
        {
          "tag": "v3.14.0",
          "kind": "minor",
          "published_at": "2025-07-23T11:29:38Z"
        },
        {
          "tag": "v3.13.0",
          "kind": "minor",
          "published_at": "2025-07-16T14:56:57Z"
        },
        {
          "tag": "v3.12.0",
          "kind": "minor",
          "published_at": "2025-07-09T15:52:55Z"
        },
        {
          "tag": "v3.11.0",
          "kind": "minor",
          "published_at": "2025-06-25T11:53:28Z"
        },
        {
          "tag": "v3.10.0",
          "kind": "minor",
          "published_at": "2025-05-28T16:04:22Z"
        },
        {
          "tag": "v3.9.0",
          "kind": "minor",
          "published_at": "2025-05-20T18:02:03Z"
        },
        {
          "tag": "v3.8.0",
          "kind": "minor",
          "published_at": "2025-05-07T17:52:59Z"
        },
        {
          "tag": "v3.7.0",
          "kind": "minor",
          "published_at": "2025-04-24T18:35:49Z"
        },
        {
          "tag": "v3.6.1",
          "kind": "patch",
          "published_at": "2025-04-10T15:50:09Z"
        },
        {
          "tag": "v3.6.0",
          "kind": "minor",
          "published_at": "2025-04-09T19:26:43Z"
        },
        {
          "tag": "v3.5.1",
          "kind": "patch",
          "published_at": "2025-03-27T19:38:38Z"
        },
        {
          "tag": "v3.5.0",
          "kind": "minor",
          "published_at": "2025-03-24T13:21:25Z"
        },
        {
          "tag": "v3.4.0",
          "kind": "minor",
          "published_at": "2025-03-11T20:36:12Z"
        },
        {
          "tag": "v3.3.0",
          "kind": "minor",
          "published_at": "2025-02-20T22:57:17Z"
        },
        {
          "tag": "v3.2.0",
          "kind": "minor",
          "published_at": "2025-02-07T17:30:25Z"
        },
        {
          "tag": "v3.1.0",
          "kind": "minor",
          "published_at": "2025-01-24T17:00:35Z"
        },
        {
          "tag": "v3.0.0",
          "kind": "major",
          "published_at": "2025-01-10T17:37:58Z"
        },
        {
          "tag": "v2.24.0",
          "kind": "minor",
          "published_at": "2024-12-18T15:40:21Z"
        },
        {
          "tag": "v2.23.1",
          "kind": "patch",
          "published_at": "2024-11-28T19:12:50Z"
        },
        {
          "tag": "v2.23.0",
          "kind": "minor",
          "published_at": "2024-11-27T16:39:19Z"
        },
        {
          "tag": "v2.22.0",
          "kind": "minor",
          "published_at": "2024-11-13T16:42:33Z"
        },
        {
          "tag": "v2.21.0",
          "kind": "minor",
          "published_at": "2024-10-23T15:38:47Z"
        },
        {
          "tag": "v2.20.1",
          "kind": "patch",
          "published_at": "2024-10-11T11:57:31Z"
        },
        {
          "tag": "v2.20.0",
          "kind": "minor",
          "published_at": "2024-10-10T09:02:11Z"
        },
        {
          "tag": "v2.19.0",
          "kind": "minor",
          "published_at": "2024-09-24T19:52:11Z"
        },
        {
          "tag": "v2.18.0",
          "kind": "minor",
          "published_at": "2024-09-11T18:27:14Z"
        },
        {
          "tag": "v2.17.1",
          "kind": "patch",
          "published_at": "2024-09-03T14:00:08Z"
        },
        {
          "tag": "v2.17.0",
          "kind": "minor",
          "published_at": "2024-08-28T14:37:31Z"
        },
        {
          "tag": "v2.16.0",
          "kind": "minor",
          "published_at": "2024-08-12T20:30:20Z"
        },
        {
          "tag": "v2.15.0",
          "kind": "minor",
          "published_at": "2024-07-31T12:57:06Z"
        },
        {
          "tag": "v2.14.1",
          "kind": "patch",
          "published_at": "2024-07-15T09:47:07Z"
        },
        {
          "tag": "v2.14.0",
          "kind": "minor",
          "published_at": "2024-07-11T12:47:36Z"
        },
        {
          "tag": "v2.13.0",
          "kind": "minor",
          "published_at": "2024-07-03T17:18:15Z"
        },
        {
          "tag": "v2.12.2",
          "kind": "patch",
          "published_at": "2024-06-19T18:57:40Z"
        },
        {
          "tag": "v2.12.1",
          "kind": "patch",
          "published_at": "2024-06-19T13:55:54Z"
        },
        {
          "tag": "v2.12.0",
          "kind": "minor",
          "published_at": "2024-06-19T09:46:22Z"
        },
        {
          "tag": "v2.11.0",
          "kind": "minor",
          "published_at": "2024-06-07T23:06:41Z"
        },
        {
          "tag": "v2.10.1",
          "kind": "patch",
          "published_at": "2024-05-22T22:16:50Z"
        },
        {
          "tag": "v2.10.0",
          "kind": "minor",
          "published_at": "2024-05-22T16:52:40Z"
        },
        {
          "tag": "v2.9.0",
          "kind": "minor",
          "published_at": "2024-05-18T06:28:46Z"
        },
        {
          "tag": "v2.8.0",
          "kind": "minor",
          "published_at": "2024-05-08T15:08:36Z"
        },
        {
          "tag": "v2.7.0",
          "kind": "minor",
          "published_at": "2024-04-24T15:41:36Z"
        },
        {
          "tag": "v2.6.0",
          "kind": "minor",
          "published_at": "2024-03-19T20:01:04Z"
        },
        {
          "tag": "v2.5.0",
          "kind": "minor",
          "published_at": "2024-03-12T22:20:42Z"
        },
        {
          "tag": "v2.4.1",
          "kind": "patch",
          "published_at": "2024-02-20T21:35:32Z"
        },
        {
          "tag": "v2.4.0",
          "kind": "minor",
          "published_at": "2024-02-20T20:42:37Z"
        },
        {
          "tag": "v2.3.2",
          "kind": "patch",
          "published_at": "2024-02-06T18:51:02Z"
        },
        {
          "tag": "v2.3.1",
          "kind": "patch",
          "published_at": "2024-01-23T18:50:58Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2024-01-16T16:16:17Z"
        },
        {
          "tag": "v2.2.0",
          "kind": "minor",
          "published_at": "2024-01-09T16:58:21Z"
        },
        {
          "tag": "v2.1.5",
          "kind": "patch",
          "published_at": "2023-12-21T14:29:07Z"
        },
        {
          "tag": "v2.1.4",
          "kind": "patch",
          "published_at": "2023-12-08T13:50:00Z"
        },
        {
          "tag": "v2.1.3",
          "kind": "patch",
          "published_at": "2023-12-08T11:00:18Z"
        },
        {
          "tag": "v2.1.2",
          "kind": "patch",
          "published_at": "2023-12-04T20:56:59Z"
        },
        {
          "tag": "v2.1.1",
          "kind": "patch",
          "published_at": "2023-11-25T11:44:29Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2023-11-11T01:36:42Z"
        },
        {
          "tag": "v2.0.13",
          "kind": "patch",
          "published_at": "2023-10-27T23:55:34Z"
        },
        {
          "tag": "v2.0.12",
          "kind": "patch",
          "published_at": "2023-10-03T03:01:59Z"
        },
        {
          "tag": "v2.0.11",
          "kind": "patch",
          "published_at": "2023-09-12T14:41:55Z"
        },
        {
          "tag": "v2.0.10",
          "kind": "patch",
          "published_at": "2023-08-24T20:23:11Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "c9ebc4f47b33d470bd739fa9930fd391df565112",
          "body": "…ng MockedStatic (#1060)\n\n* MockitoWhenOnStaticToMockStatic: name unnamed `_` variable when reusing MockedStatic\n\nWhen a `MockedStatic` was held in an unnamed variable\n(`try (var _ = Mockito.mockStatic(A.class))`), the recipe emitted\n`_.when(...)`, which does not compile. Rename the resource to a ge\n[…]\n on Java 25 with `rewrite-java-25` so unnamed `_` variables parse.\n\n* Add test for renaming multiple unnamed variables individually\n\n* Drop Java 25 build bump; to be handled in a separate migration PR",
          "is_bot": false,
          "headline": "MockitoWhenOnStaticToMockStatic: name unnamed `_` variable when reusi…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-25T07:18:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4534e081c149054069956237864dd6e3e81844b3",
          "body": "Swap the test parser from `rewrite-java-21` to `rewrite-java-25` and override\nthe toolchain to 25 (the `recipe-library` plugin pins it to 21), mirroring\nrewrite-migrate-java. CI now provisions JDK 25 and 21 via the shared workflow.\n\nThe three java-21 -> java-25 fallout failures (AssertToAssertions,\n\n[…]\nl caused by the\nparser dropping a method invocation's type when an argument type was\nunresolvable. That is now fixed upstream in openrewrite/rewrite#8318, so no\nrecipe or test changes are needed here.",
          "is_bot": false,
          "headline": "Build and test on Java 25 with rewrite-java-25 (#1061)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-25T07:07:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b1bbe20fc81f3ad8acf12577cb378607b6c0a50e",
          "body": "* HamcrestMatcherToJUnit5: support Kotlin via KotlinTemplate branches\n\n* Attribute Kotlin types after KotlinTemplate instead of relaxing validation\n\n* Explain immutableExecutionContext(false) in notEqualToKotlin test\n\n* Clarify why only Kotlin needs post-template type attribution\n\n* Remove Kotlin ty\n[…]\nng) vs assertX(condition, Supplier) overloads\nstay ambiguous when the reason is a substituted placeholder, leaving the call\nuntyped so the static import is dropped. Attribute just that enclosing call.",
          "is_bot": false,
          "headline": "HamcrestMatcherToJUnit5: support Kotlin sources (#1058)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-25T07:06:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8cfaee5af4b1d232b8b1e74802a33b9b89d30249",
          "body": "…n't be rendered (#1066)\n\nAnonymous and local class types (proper JavaType.Class, not JavaType.Unknown)\nrender an invalid declared type such as `MyTest.1 x = ...`, which makes the\nextraction template generate the wrong number of statements and crashes the run.\nThe #1059 guard only covered Unknown/None/Void/Null. Guard the template apply so\nany such failure leaves the argument inline instead of failing the recipe.",
          "is_bot": false,
          "headline": "Leave AssertThrowsOnLastStatement arguments inline when their type ca…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-24T16:38:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "acfa18f0d12cf25273eb6e63425a5eae2fcd57c6",
          "body": "* refactor: extract shared TestNgAsserts.isFloatingPointType helper\n\n* feat: add TestNgAssertionToAssertJ assertEquals/assertNotEquals mapping\n\n* feat: cover boolean/null/same/noOrder/fail in TestNgAssertionToAssertJ\n\n* feat: remove dead local Assertion instance after rewriting its calls\n\n* feat: wi\n[…]\n contextSensitive and dead Supplier import\n\n* refactor: drop unnecessary contextSensitive and dead Supplier import in TestNgSoftAssertToAssertJ\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Migrate TestNG Assertion (instance) to AssertJ (#1064)",
          "author_name": "Julien Herr",
          "author_login": "juherr",
          "committed_at": "2026-07-24T16:25:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5fe4cadeb3626da64c1b2246daf86411ef748697",
          "body": "…1065)\n\n* AssertTrueNullToAssertNull: support Kotlin via LST manipulation\n\nReplace the contextSensitive JavaTemplate, which generated 0 statements on a\nKotlin LST, with direct LST manipulation (rename to assertNull, rewrite the\nfirst argument, and fix the method type). This works on both Java and Ko\n[…]\ned an empty body on\nKotlin, throwing IndexOutOfBounds on getStatements().get(0). Leave the JUnit 4\n@Test(expected = ..., timeout = ...) intact on Kotlin rather than crashing or\ndropping the arguments.",
          "is_bot": false,
          "headline": "Support Kotlin in three JUnit testing recipes (fix crashes/no-ops) (#…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-24T15:49:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d7134028d6323946e311772fa15255b1fcf4e93d",
          "body": "Add the `TestNgSoftAssertToAssertJ` recipe and wire it into the\n`TestNgToAssertj` declarative recipe so that TestNG soft assertions\n(`org.testng.asserts.SoftAssert`) are converted to AssertJ soft\nassertions (`org.assertj.core.api.SoftAssertions`).\n\nThe recipe rewrites the instance assertion calls\n(`\n[…]\nnAnyOrder).\n\nAlso broaden the assertj-core `onlyIfUsing` dependency trigger from\n`org.testng.*` to `org.testng..*` so classes using only\n`org.testng.asserts.SoftAssert` still get the dependency added.",
          "is_bot": false,
          "headline": "feat: migrate TestNG SoftAssert to AssertJ SoftAssertions (#1063)",
          "author_name": "Julien Herr",
          "author_login": "juherr",
          "committed_at": "2026-07-24T10:13:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7ee9a16af02c36dbf459ea74241a2130f86076b4",
          "body": "RemoveNotMatcherVisitor was invoked via visit(tree, ctx), which roots the\nsub-visitor at Cursor.ROOT. When a not() matcher wraps a bare value instead\nof another Hamcrest matcher, the visitor wraps it in equalTo() and calls\nmaybeAddImport, whose ImportService lookup walks the cursor for an enclosing\n\n[…]\nceFile parent found\". Pass the current\ncursor as the parent so the sub-visitor inherits a path with the\nJavaSourceFile ancestor. Applied to both HamcrestMatcherToJUnit5 and\nHamcrestInstanceOfToJUnit5.",
          "is_bot": false,
          "headline": "Fix Hamcrest not() unwrap crash when wrapping a bare value (#1062)",
          "author_name": "Jammy Louie",
          "author_login": "Jammy-Louie",
          "committed_at": "2026-07-23T22:30:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5a35eafd825d264777056d47fdca1d016065c86b",
          "body": "…(#1057)",
          "is_bot": false,
          "headline": "AssertTrueNegationToAssertFalse: support Kotlin via LST manipulation …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-23T15:57:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c56011d863784b1bc114d70280a33b7a9d357c63",
          "body": "…(#1059)\n\nExtracting a last-statement argument whose type is `None`, `Unknown`,\n`void` or `null` rendered an invalid variable declaration (an empty type\nproduced a `J.Assignment`, causing `J$Assignment cannot be cast to\nJ$VariableDeclarations`; `void`/`<unknown>` produced templates that\ngenerated no statements). Such types occur when the `assertThrows` lambda\nwas just synthesized earlier in the same run (e.g. from `@Test(expected)`).\nLeave those arguments inline instead of extracting them.",
          "is_bot": false,
          "headline": "Fix AssertThrowsOnLastStatement crash on unattributed argument types …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-23T15:45:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "acb0b002114224e56c8cea7f37b041e40cadb64c",
          "body": null,
          "is_bot": false,
          "headline": "Set Develocity `projectId` to \"openrewrite\" (#1056)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-22T15:05:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "81d6e6708db98ddf38639a0c9ff21b865fe4228e",
          "body": "Co-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Move Develocity to community.develocity.cloud",
          "author_name": "tim@moderne.io",
          "author_login": "timtebeek",
          "committed_at": "2026-07-22T12:14:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cefa93583d858576e8a30bfa947158f0933b349c",
          "body": "Co-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "timtebeek@gmail.com",
          "author_login": "timtebeek",
          "committed_at": "2026-07-22T09:40:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cdad6240d97dac27787786adbcd4a45ec7b2588e",
          "body": "… (#1055)\n\nApplying the recipe to Kotlin test sources threw\n\"Expected a template that would generate exactly one statement to\nreplace one statement, but generated 0\" because Java statement\ntemplates produce no statements on a Kotlin LST.\n\nBranch on K.CompilationUnit at each template site:\n- private \n[…]\nch template apply.\nClass-wide autoformat is skipped for Kotlin sources since it would\nreformat unrelated code across the whole class.\n\nFixes https://github.com/moderneinc/customer-requests/issues/2825",
          "is_bot": false,
          "headline": "ReplaceInitMockToOpenMock: support Kotlin via KotlinTemplate branches…",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-07-21T12:36:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f0f55abf1414ab1f3dd44ab5fa2b2ffffcd24d65",
          "body": "* Fix infinite loop unwrapping non-Matchers not() in Hamcrest JUnit5 recipes\n\n* Migrate CoreMatchers not()/instanceOf()/isA() in Hamcrest JUnit5 recipes",
          "is_bot": false,
          "headline": "Fix Hamcrest not() unwrap infinite loop and support CoreMatchers (#1054)",
          "author_name": "Jammy Louie",
          "author_login": "Jammy-Louie",
          "committed_at": "2026-07-20T13:37:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fa03dcc06b84f89411e0b358c64dd3ac659092bf",
          "body": "Co-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "timtebeek@gmail.com",
          "author_login": "timtebeek",
          "committed_at": "2026-07-20T09:21:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "224c5ebb42ea181c2579f7047e4cd3130a29669f",
          "body": "…053)\n\n* AssertEqualsNullToAssertNull: support Kotlin via LST manipulation\n\n* AssertEqualsNullToAssertNull: simplify argument rewrite via ListUtils\n\n* AssertEqualsNullToAssertNull: drop redundant method type null check",
          "is_bot": false,
          "headline": "AssertEqualsNullToAssertNull: support Kotlin via LST manipulation (#1…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-17T10:49:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cc1032964e35f82c5ba98e9340c40b2294160c0e",
          "body": "TEST_ANNOTATION_PATTERN's `*Test*` segment matches any simple\nannotation name containing \"Test\", which incidentally also matches\n`@TestFactory`. Unlike `@Test`/`@ParameterizedTest`/`@RepeatedTest`/\n`@TestTemplate`, `@TestFactory` methods are required by JUnit Jupiter\nto return a value (e.g. `Collect\n[…]\n their\nreturn type to `Unit` produces a Kotlin type-mismatch compile error.\n\nAdd an explicit exclusion for `@TestFactory` and a regression test.\n\nCo-authored-by: Neil Mushell <neil.mushell@airbnb.com>",
          "is_bot": false,
          "headline": "KotlinTestMethodsShouldReturnUnit: exclude @TestFactory methods (#1052)",
          "author_name": "neil-mushell",
          "author_login": "neil-mushell",
          "committed_at": "2026-07-14T21:58:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2b5d8526dc226ff4794716133b2d0780eb257530",
          "body": "…builds\n\nDrop `newVersion: 3.x` from the `mockito-all` -> `mockito-core`\n`ChangeDependency` step. For a version managed by a parent POM,\n`ChangeDependency` did not see the child inherit the renamed\n`mockito-core` management within the same run, so it hardcoded an\nexplicit version onto the child depe\n[…]\n3.x` bumps\nthe version. Managed child dependencies stay versionless and the recipe\nconverges in a single cycle, matching the `mockito-inline` -> `mockito-core`\npattern already used in Mockito4to5Only.",
          "is_bot": false,
          "headline": "Keep Mockito dependency managed in Mockito1to3Migration multi-module …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-07-13T08:10:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e2dffcaf7ea1486cc10c23e5a45444fec5ab453f",
          "body": "Co-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "timtebeek@gmail.com",
          "author_login": "timtebeek",
          "committed_at": "2026-07-13T07:45:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c709cec39a30ef12990715b5d96501604f16ade9",
          "body": "With openrewrite/rewrite#8229, a plain '17' is canonicalized to '[17,)'\nby HasMinimumJavaVersion, so the explicit range wrapper is no longer\nnecessary and the shorter form matches how a reader would expect the\noption to be written.",
          "is_bot": false,
          "headline": "Simplify JUnit5to6Migration minimum Java version to '17' (#1050)",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-07-10T15:26:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d3830c04a67edd51b3d9784c6429dfab7997b356",
          "body": "…#1049)\n\nA bare version like '17' is parsed as an exact-match selector by\nSemver, so the precondition returns false on repos declaring a higher\nJava version (e.g. 21) and the whole recipe is silently skipped.\nSwitching to '[17,)' makes the intent match the recipe's name.",
          "is_bot": false,
          "headline": "Use range selector so JUnit5to6Migration gates on Java 17 or higher (…",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-07-10T13:29:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b0b1f19daed5e26989059ed4b49edb2a43e3dd6c",
          "body": "`AssertThrowsOnLastStatement` is a `JavaTemplate`-based recipe, and Java\nvisitors also run over Kotlin because `K.CompilationUnit` implements\n`JavaSourceFile`. On a Kotlin file the recipe emitted Java syntax for the\nextracted variable declaration, producing invalid code such as\n`baz: String = baz()`\n[…]\ntrailing call would wrap it as an expression\n(`var o = ...`) and fail to parse.\n\nGroovy and other languages remain excluded via `isAcceptable`.\n\nCo-authored-by: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Support Kotlin sources in `AssertThrowsOnLastStatement` (#1048)",
          "author_name": "Vladimir Sitnikov",
          "author_login": "vlsi",
          "committed_at": "2026-07-10T13:17:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2e6d48581b900086309249b4fbd62c25d6764cb9",
          "body": "Co-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "tim@moderne.io",
          "author_login": "timtebeek",
          "committed_at": "2026-07-03T08:57:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "95ff73ab69a3ee2e0be0311b77f94a0179e54c41",
          "body": "… incomplete (#1047)",
          "is_bot": false,
          "headline": "Keep `mockStatic` import in `CleanupMockitoImports` when type info is…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-23T07:48:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cba9383053f52d6d58f7af0201ff2a1da5cff673",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/builder/yESIGzVB0?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-23T07:38:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9321cbc3968dd4974ce0bcbfe5eebfa04d9202f6",
          "body": "* Convert `assertThrows` with an `Executable` variable, retyping to `ThrowingCallable`\n\nFixes #511\n\n* Address review: inline safe via reduce; drop unused import; hoist ThrowingCallable type",
          "is_bot": false,
          "headline": "Convert `assertThrows` with an `Executable` variable (#1046)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T16:38:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0eb4765dce528e4076e2f2faf5b616bd672700d4",
          "body": "…sent (#1043)\n\nWhen a method carries both a JUnit 4 lifecycle annotation (e.g. @Before)\nand its Jupiter equivalent (@BeforeEach), changing the type of the JUnit 4\nannotation produced a duplicate, non-repeatable annotation. Remove the\nJUnit 4 annotation instead in that case.\n\nFixes #501",
          "is_bot": false,
          "headline": "Skip JUnit 4 lifecycle annotation when Jupiter equivalent already pre…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T16:17:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e4715700c44fda54f76a32c73b18c093d9ef84aa",
          "body": "* Add DecomposeConjunctionAssertion recipe for issue #387\n\n* Add end-to-end issue #387 tests for conjunction decomposition\n\n* Use built-in Expression.unwrap() instead of a custom helper\n\n* Add test showing same-subject conjuncts collapse into a chain\n\n* Flatten conjuncts functionally with ListUtils.flatMap\n\n* Build decomposed assertions with ListUtils.flatMap",
          "is_bot": false,
          "headline": "Decompose `assertThat(a && b).isTrue()` into separate assertions (#1042)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T15:45:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8ade02dcc506c6f7caf6805769ae41e95a8639c0",
          "body": "…1041)\n\nRewrites second-based @Timeout values at or above a configurable\nthreshold (default 1000s) to the equivalent minutes, e.g.\n@Timeout(10000) -> @Timeout(value = 167, unit = TimeUnit.MINUTES),\nmaking likely millisecond mistakes visible. Implements option a) of #452\nand adds it to JupiterBestPractices.\n\nFixes #452",
          "is_bot": false,
          "headline": "Add recipe to surface implausible JUnit @Timeout values as minutes (#…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T14:39:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7e27ad6a6934642e7baae33aa0d734e67733be2a",
          "body": "Resolve #308 so that classes whose methods carry a custom annotation\nthat is itself meta-annotated with @Test (e.g. @MavenTest) also have\ntheir redundant public modifier removed.\n\nUse AnnotationMatcher's built-in meta-annotation matching rather than\nhand-rolled recursion.",
          "is_bot": false,
          "headline": "TestsShouldNotBePublic: recognize meta-annotated test methods (#1040)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T14:28:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db78ffdbc2bbf64aa55c7646c3603f7d34765586",
          "body": "…compareTo (#1039)\n\n* Cover previously-commented AssertJ best-practice tests\n\nUn-comments the disabled cases in AssertJBestPracticesTest and closes the\ncoverage gaps they represented:\n\n- Comparable relational operators (>, >=, <, <=) were already handled by\n  AssertJPrimitiveRules; the cases were on\n[…]\nth object\n  and primitive arrays. Ordered before AssertJNumberRules to avoid the\n  `isGreaterThanOrEqualTo(1)` -> `isPositive()` collision.\n\n* Update recipes.csv for new recipe and declarative entries",
          "is_bot": false,
          "headline": "Cover commented AssertJ tests: array length, Comparable & BigDecimal …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T14:01:38Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9a8b7cb872efbe08a0cfdd2e38d4f124c8f98c3c",
          "body": "* Add SimplifyAssertJNullRelatedAssertion to fix isSameAs(null) compile error\n\nassertThat(null == a).isEqualTo(true) was converted by the picnic\nAssertJObjectRulesRecipes into the uncompilable assertThat(null).isSameAs(a),\nsince the Refaster rule binds the left operand as the actual value.\n\nThis add\n[…]\nJInstanceOfAssertion. Only comparisons against the\nnull literal are handled; genuine x == y still becomes isSameAs(y).\n\nFixes #868\n\n* Use J.Literal.isLiteralValue(expr, null) instead of a local helper",
          "is_bot": false,
          "headline": "Fix isSameAs(null) compile error from null reference comparisons (#1038)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T13:29:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9e69b1bb2cb4e624b502b6888cf6484cbb5183b8",
          "body": "The existing rule targeted java.util.Collection, so an actual typed as\nthe broader java.lang.Iterable was not matched. Widen the required type\nto java.lang.Iterable, which subsumes the Collection case.\n\nFixes #811",
          "is_bot": false,
          "headline": "Simplify AssertJ hasSize(0) to isEmpty() for Iterable (#1037)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T12:42:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cd8b263729a342b628a4168de887ad1e3fd7f3eb",
          "body": "…in a single run (#1036)\n\nThe Assertj aggregate normalizes primitive isSameAs/isNotSameAs to\nisEqualTo/isNotEqualTo via Picnic's AssertJPrimitiveRulesRecipes, after\nwhich our type-specific rules collapse isEqualTo(0) to isZero(), etc.\nBecause between-cycle re-attribution does not give the rewritten \n[…]\nso match the isSameAs/isNotSameAs forms directly so\nthey collapse straight to isZero/isNotZero/isOne, and order them before\nAssertJPrimitiveRulesRecipes so they win within a single cycle.\n\nFixes #1032",
          "is_bot": false,
          "headline": "Converge AssertJ isSameAs/isNotSameAs(0|1) to isZero/isNotZero/isOne …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T12:21:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "286a3d7dea0605274987b21def46d1c80320ba6c",
          "body": "…stanceOf(Type.class) (#1035)\n\n* Add SimplifyAssertJInstanceOfAssertion recipe\n\nConvert `assertThat(x instanceof Type).isTrue()` to the dedicated\n`assertThat(x).isInstanceOf(Type.class)` (and `.isFalse()` / negated\nforms to `isNotInstanceOf`), so failures describe the actual type\ninstead of just \"ex\n[…]\nd and\nstatically imported `assertThat`, array types, and skips Java 16+\npattern bindings whose variable may be used later.\n\nFixes #1030\n\n* Regenerate recipes.csv for SimplifyAssertJInstanceOfAssertion",
          "is_bot": false,
          "headline": "Refactor assertThat(x instanceof Type).isTrue() to assertThat(x).isIn…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T12:05:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9a16f5707d93a6a0cbe04b5ef7ea3ccd4abf0ee8",
          "body": "Handles literal-first AssertJ size assertions where the meaningful\nexpression sits on the isEqualTo(...) side, e.g.\nassertThat(4).isEqualTo(coll.size()) -> assertThat(coll).hasSize(4).\n\nThis is the always-safe \"Case A\" from the issue: the comparison is on a\nprimitive int (size()/length()/array length), so reversing the\nassertion is behavior-preserving. Object-equality reversal (Case B) is\nintentionally left untouched since it depends on equals() symmetry.\n\nFixes #1031",
          "is_bot": false,
          "headline": "Add SimplifyHasSizeFromIsEqualToAssertion recipe (#1034)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T11:58:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "54f3db8e472f9bd37dccd030fc1671b3f9728ad2",
          "body": "Normalize the AssertJ entry points introduced by TestNgToAssertj to a\nstatic import via UseStaticImport, so qualified Assert.assertX(...) calls\nbecome static-imported assertThat(...) instead of mirroring the source\nqualification as Assertions.assertThat(...).\n\nFixes #1029",
          "is_bot": false,
          "headline": "TestNgToAssertj: emit a static import for assertThat (#1033)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-20T11:56:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a8a3615d8a91364d50826349f535f128af26394f",
          "body": "…#1016)\n\nWires org.openrewrite.java.AddCommentToMethodInvocations into\nReplacePowerMockito after the Whitebox migration and before the PowerMock\ndependency removal, matching `org.powermock.reflect.Whitebox *(..)`. Any\nremaining Whitebox call with no faithful reflection equivalent (type-based\nget/set\n[…]\nn a bespoke one. Adds an\nend-to-end ReplacePowerMockitoIntegrationTest case (with powermock-reflect-1\non the classpath) proving migrate-and-flag together.\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Flag un-migratable PowerMock powermock-reflect usage with a comment (…",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-19T13:37:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "665bd845b0491b6dc8b08d4fd46bddd4385caeda",
          "body": "* Expand TestNG to AssertJ assertion coverage\n\nAdd dedicated recipes for assertSame/assertNotSame, assertThrows/expectThrows,\nassertEqualsNoOrder, and assertEqualsDeep/assertNotEqualsDeep, and fix\nassertEquals/assertNotEquals to use element-wise assertions (containsExactly,\ncontainsExactlyElementsOf\n[…]\nare still\nvisited and migrated. Brings the TestNG recipes and JUnitFailToAssertJFail\nin line with the recursing pattern already used elsewhere.\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Expand TestNG to AssertJ assertion coverage (#1028)",
          "author_name": "Julien Herr",
          "author_login": "juherr",
          "committed_at": "2026-06-19T12:53:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b700d7b216ad689accdf2a8a69b290724d685cd0",
          "body": "Whitebox.invokeConstructor(Class, args...) -> Constructor lookup +\nnewInstance() on the named class:\n\n    Constructor<MyService> c = MyService.class.getDeclaredConstructor(String.class);\n    c.setAccessible(true);\n    MyService s = c.newInstance(\"Alice\");\n\nHandles both the Object... varargs overload\n[…]\ncomposite.\n\nReuses the base hasArrayArg helper; the constructor resolution and class-literal\nhelpers are kept private to the recipe, matching the existing InvokeMethod\nrecipe (no shared-base changes).",
          "is_bot": false,
          "headline": "Add PowerMockWhiteboxInvokeConstructorToJavaReflection recipe (#1027)",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-18T16:17:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "20880b9e1a7a9c30b4d74587e4bd65a0cc9daa46",
          "body": null,
          "is_bot": false,
          "headline": "Add PowerMockWhiteboxGetMethodToJavaReflection recipe (#1026)",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-18T15:32:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97acbeedf12110585e8cc7838ccce46bef0af814",
          "body": "Parsing the useJUnitPlatform() snippet spins up the Groovy compiler and\nresolves the Gradle classpath on every call, even though the result\ndepends only on whether `.configureEach` is used. Cache the parsed\ntemplate on the run-scoped ExecutionContext so the parse happens once per\nrun instead of once per modified build script, and hand back a copy with\nfresh ids on each use to keep tree ids unique.",
          "is_bot": false,
          "headline": "Cache parsed Gradle template in GradleUseJunitJupiter",
          "author_name": "Knut Wannheden",
          "author_login": "knutwannheden",
          "committed_at": "2026-06-18T06:26:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5e4e776179bd62670aa6a9f34ca3d52fa5b7f63b",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.xml.security.RemoveOwaspSuppressions?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D#defaults=W3sidmFsdWUiOiIyMDI2LTA2LTE3IiwibmFtZSI6ImN1dE9mZkRhdGUifV0=\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Remove out-of-date OWASP suppressions",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-17T17:37:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "96f93ccae5e88e21ff871d4841aca506d57953c2",
          "body": "Whitebox.getField(Class, \"name\") returns a reflective Field; migrate it to\nClass.getDeclaredField(\"name\") + setAccessible(true), reusing the result\nvariable as the local. Registered in the PowerMockWhiteboxToJavaReflection\ncomposite.\n\nA shared resultLocalName helper (reuse the declared result variable when the\ncall's result IS the reflective object) is added to WhiteboxToReflectionVisitor.",
          "is_bot": false,
          "headline": "Add PowerMockWhiteboxGetFieldToJavaReflection recipe (#1025)",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-17T10:35:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "862bd49266051972948d6ad84e29ab150d31a4de",
          "body": "…ion (#1024)\n\nField.get and Method.invoke return Object, so a primitive-typed result\ndeclaration emitted a `(int) object` cast, which does not compile. Cast\nto the wrapper type instead; the surrounding assignment auto-unboxes.",
          "is_bot": false,
          "headline": "Box primitive casts in Whitebox getInternalState/invokeMethod reflect…",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-17T07:01:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9e86af9ea65f776ede1cfe3f0dd431f50acce57a",
          "body": "…021)",
          "is_bot": false,
          "headline": "Refactor `PowerMockWhiteboxToJavaReflection` into per-API recipes (#1…",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-17T05:54:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "30c392dc5a2b6d7eca5e5fbc6051b2fa4db231c7",
          "body": "…(#1023)\n\nThe where-overload supplies the declaring class to look the field up on.\ngetDeclaredField does not walk the class hierarchy, so for an inherited\nfield child.getClass().getDeclaredField(\"name\") throws NoSuchFieldException;\nonly Parent.class.getDeclaredField(\"name\") resolves it.\n\nGenerate th\n[…]\nything else\n(setAccessible, field.set, throws Exception, Field import) is identical to\nthe existing 3-arg path. The Class-typed placeholder also accepts a\nnon-literal Class<?> variable as the 4th arg.",
          "is_bot": false,
          "headline": "Support 4-arg Whitebox.setInternalState(target, field, value, Class) …",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-16T16:47:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c9cd3bd9cb61feb433e31d1537a175130e296521",
          "body": "…t (#1022)\n\nUsesType matches against fully qualified type names, but the precondition was\ngiven the two-token \"org..* *Test*\" pattern that AnnotationMatcher uses. That\nonly ever matched because of a TypeNameMatcher bug where a ..* token followed by\na suffix matched every name, fixed upstream in #8012. Use a valid single-token\nglob org.junit..*Test* for the UsesType precondition; the AnnotationMatcher\npattern is unchanged.",
          "is_bot": false,
          "headline": "Fix UsesType precondition pattern in KotlinTestMethodsShouldReturnUni…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-16T12:19:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a602b445dae2fc1f5e11f11fdf17980f324ce016",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/builder/abtSSjvHK?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-16T10:56:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ff368ff6414b25a8dd1025ed3f233ec0ca5a2ebb",
          "body": "…ange (#1020)",
          "is_bot": false,
          "headline": "Update UpdateMockWebServerMockResponse test for nested-type render ch…",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-06-15T16:36:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "45342dcf77b3fe244b7728afe49802773b44258d",
          "body": "Disabled in d1e6bbba after teardown was injected in the wrong scope for\n@Nested classes; #742 fixed nested-class and qualified-class-name\nhandling. Re-enabling now that the underlying issue is resolved.",
          "is_bot": false,
          "headline": "Re-include CloseUnclosedStaticMocks in Mockito1to4Migration (#1019)",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-06-15T16:17:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e1cb892576518def734591baa3eb3de152b9b792",
          "body": "…n Testcontainers 2.x (#1014)\n\nTestcontainers 2.x removed the nested `LocalStackContainer.Service` enum and `getEndpointOverride(...)`. The existing `Testcontainers2Migration` only renamed the type, leaving `LocalStackContainer.Service.SQS` and `getEndpointOverride(...)` as unresolved references (Ja\n[…]\n alike).\n\nAdd a `Testcontainers2LocalStack` step (run before the type rename) that replaces `Service` constants with their service-name strings and `getEndpointOverride(service)` with `getEndpoint()`.",
          "is_bot": false,
          "headline": "Migrate removed LocalStack `Service` enum and `getEndpointOverride` i…",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-06-12T09:28:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3934d1e33f7f66a699eda99c17fbe87451451d2b",
          "body": "…ing JUnit 6 migration (#1018)",
          "is_bot": false,
          "headline": "Add junit-platform-launcher testRuntimeOnly dependency for Gradle dur…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-11T08:02:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d5554a9716c10230c4a8465c425d6b6075e46c5e",
          "body": "* Don't add MockitoExtension during migration when unsafe (#875)\n\n`AddMockitoExtensionIfAnnotationsUsed` was adding `@ExtendWith(MockitoExtension.class)`\neven when Mockito support was already provided another way, breaking tests.\n\n- Remove `AddMockitoExtensionIfAnnotationsUsed` and its companion\n  `\n[…]\nn checks.\n\n* Use J.ClassDeclaration#hasModifier for the abstract check\n\n* Use org.mockito.* wildcard for the Mockito-annotation checks\n\n* Use org.mockito.* wildcard in PowerMockRunnerDelegateToRunWith",
          "is_bot": false,
          "headline": "Don't add MockitoExtension during migration when unsafe (#875) (#1017)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-10T11:05:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6dc35d44f851b8bf7252e086ce4ddf6c8e761c4a",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.AddToGitignore?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D#defaults=W3sidmFsdWUiOiIuY29udGV4dC8iLCJuYW1lIjoiZW50cmllcyJ9XQ==\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "git-ignore .context/",
          "author_name": "Greg Oledzki",
          "author_login": "greg-at-moderne",
          "committed_at": "2026-06-09T11:31:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5b719c203a9206859e4b7e9456ec5503897cd121",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.ExamplesExtractor?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Extract documentation examples from tests",
          "author_name": "Greg Oledzki",
          "author_login": "greg-at-moderne",
          "committed_at": "2026-06-03T12:30:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1ca62235d5c5d13b5929374dad5a3ff4fd702a12",
          "body": null,
          "is_bot": false,
          "headline": "Match MockWebServer.shutdown() against pre-rename type (#1012)",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-06-02T19:43:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "947998830d5ca8e041472a3e7b1d0ee75c12c047",
          "body": "…ed from parent pom (#1011)",
          "is_bot": false,
          "headline": "Add direct mockwebserver3 dep when only old mockwebserver dep inherit…",
          "author_name": "Steve Elliott",
          "author_login": "steve-aom-elliott",
          "committed_at": "2026-06-02T15:52:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4d31fbb472e259a0bc8c7901654a63e9b1692428",
          "body": "…tions (#1010)\n\nAssertJ deprecated `AssertionsForClassTypes` and `AssertionsForInterfaceTypes`\nin favor of the unified `Assertions` entry point (assertj/assertj#4268).\n\nThis adds `MigrateAssertionsForClassAndInterfaceTypes`, which retargets all\nstatic methods of both classes to `Assertions`. The gen\n[…]\nrable`/... argument to e.g. `IterableAssert`\nand stop compiling. The new type-aware `MigrateAssertionsForClassTypes` recipe\nroutes only those colliding arguments to `assertThatObject`, resolving #664.",
          "is_bot": false,
          "headline": "Add recipe to migrate AssertionsForClassTypes/InterfaceTypes to Asser…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-06-01T08:20:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7cbff2a3ef15e4bd2ca3bcbb8f7d8b4554b36763",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZQ%3D%3D\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-29T08:57:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "86f2308c5646fbbd789458a81b15903165779a0a",
          "body": "…lable code (#1009)\n\n* fix: retain doNothing() stubs inside lambda bodies to prevent uncompilable empty lambda body\n\n* fix: retain doNothing() stubs in expression lambda and switch expression arms to prevent uncompilable code\n\n* Update src/main/java/org/openrewrite/java/testing/mockito/RemoveDoNothi\n[…]\nSwitchExpressionArm() into the single call site and removes the unnecessary mi != null check.\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>\nCo-authored-by: Tim te Beek <timtebeek@gmail.com>",
          "is_bot": false,
          "headline": "fix: retain doNothing() stubs inside lambda bodies to prevent uncompi…",
          "author_name": "darrencilia",
          "author_login": "darrencilia",
          "committed_at": "2026-05-28T22:18:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "45d25287c16359548f1e24b24a4e3775bc5535a0",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=QUxML01vZGVybmUgKyBPcGVuUmV3cml0ZS9PcGVuUmV3cml0ZQ%3D%3D#defaults=W3sidmFsdWUiOiI5LjUuMSIsIm5hbWUiOiJ2ZXJzaW9uIn0seyJ2YWx1ZSI6ImJpbiIsIm5hbWUiOiJkaXN0cmlidXRpb24ifSx7InZhbHVlIjoiYmFmYzE0MWI2MTlhZDYzNTBmZDk3NWZjOTAzMTU2ZGQ1YzE1MTk5OGNjOGIwNThlOGMxMDQ0YWI1ZjdiMDMxZiIsIm5hbWUiOiJkaXN0cmlidXRpb25DaGVja3N1bSJ9XQ==\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Update Gradle wrapper 9.5.1",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-28T19:57:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a632e17c18f3c9c434c3a5066790db79d33b6321",
          "body": null,
          "is_bot": false,
          "headline": "Regenerate recipes.csv (#1007)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-28T18:36:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b192ca3ca2509163516c0db32ef65a9bd80106a",
          "body": "* Add RemoveJupiterMigrationSupport recipe\n\n* Include RemoveJupiterMigrationSupport in JupiterBestPractices\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Add RemoveJupiterMigrationSupport recipe (#1006)",
          "author_name": "sullis",
          "author_login": "sullis",
          "committed_at": "2026-05-26T08:22:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f3f61a6e10ac3e2a334d6a9832455a5fbbf7d872",
          "body": "* Reuse `MockedStatic` for consecutive stubbings of the same class\n\nFixes #1004: `MockitoWhenOnStaticToMockStatic` created nested\n`try (MockedStatic<X>)` blocks for sibling `Mockito.when()` calls on\nthe same static class, which Mockito 5 rejects at runtime with\n\"static mocking is already registered in the current thread\".\n\nSubsequent same-class stubbings (siblings or nested in inner blocks)\nnow share the enclosing `MockedStatic`.\n\n* Inline single-arg overload",
          "is_bot": false,
          "headline": "Reuse `MockedStatic` for consecutive stubbings of the same class (#1005)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-26T07:22:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ec085aad2b59916869665f58813daf5d09cbfc9a",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-21T08:01:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "32c775280d6b8d7c8cfc27c8f79889596c2e0201",
          "body": null,
          "is_bot": false,
          "headline": "Handle Kotlin sources in `MockitoWhenOnStaticToMockStatic` (#1003)",
          "author_name": "Merlin Bögershausen",
          "author_login": "MBoegers",
          "committed_at": "2026-05-20T17:43:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6e33f5b9b51515babef19390975a9db136fa7aa3",
          "body": "* Add JUnit 6.1 migration recipes\n\n- Migrate `org.junit.jupiter.engine.Constants` to `org.junit.jupiter.api.Constants`\n- Rename `Executions.started()` to `finished()` for `EngineTestKit`\n- New `MigrateJUnitPioneerToJupiter` recipe for `@DefaultLocale`, `@DefaultTimeZone`,\n  and the system property a\n[…]\n JUnit 6.1 recipes\n\n* Use Lombok getters and simplify ListUtils check in RemoveJreOther\n\n* Remove redundant null/size guard in RemoveJreOther\n\n* Remove redundant reference check in stripOtherFromArray",
          "is_bot": false,
          "headline": "Add JUnit 6.1 migration recipes (#1002)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-20T15:32:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "da4dd542e87ed2cdfa1080132bae723ef4d33c1f",
          "body": "…ion (#1000)\n\nWhen the delta argument was a non-floating-point literal (e.g. `0`),\n`assertEquals(7f, getHeight(), 0)` was converted to\n`assertThat(getHeight()).as(0).isEqualTo(7f)` because the recipe checked\nthe argument's expression type instead of the resolved method's parameter\ntype. The compiler\n[…]\nrgument is a delta.\n\nNow inspects the resolved parameter type, and inserts an explicit cast on\nthe `within(...)` argument when the literal would otherwise resolve to a\nmismatched `Offset<T>` overload.",
          "is_bot": false,
          "headline": "Fix `assertEquals` delta misinterpreted as message in AssertJ convers…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-18T16:52:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0798dafa4c65aef71ecf5c204999db2a750dc1d7",
          "body": "…ourceFile\" (#1001)\n\n* Fix `EnclosedToNested` failing with `Expected to find enclosing JavaSourceFile`\n\n`RemoveAnnotationVisitor` was invoked via `visitNonNull(cd, ctx)` with no\nparent cursor, so its visit walks up an isolated cursor chain that never\nreaches the enclosing `JavaSourceFile`. Pass `get\n[…]\nn the generated\n`tearDownStaticMocks()` and the next method, matching the spacing already\npresent after `setUpStaticMocks()`. Update the expected output so the\ntest reflects the consistent formatting.",
          "is_bot": false,
          "headline": "Fix `EnclosedToNested` failing with \"Expected to find enclosing JavaS…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-18T16:51:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6316a74662d7dc424e92573acc3f771270b1c1cd",
          "body": "…ValueSource` (#998)\n\n* Preserve newlines when converting `@CsvSource(textBlock = ...)` to `@ValueSource`\n\nWhen a `@CsvSource(textBlock = ...)` is replaced by `@ValueSource`, each value is now placed on its own indented line instead of being collapsed onto a single line.\n\n* Use `autoFormat` instead of manual padding manipulation",
          "is_bot": false,
          "headline": "Preserve newlines when converting `@CsvSource(textBlock = ...)` to `@…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-16T11:15:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "28b439c284e35163530ea0fd4a5f447b2c9df6a9",
          "body": "…#996)\n\n* Add test demonstrating broken `extends Assert` migration.\n\n* Remove `extends Assert` during JUnit 4 to 5 migration\n\nWhen a class extends `org.junit.Assert`, unqualified inherited\nassertion calls otherwise shadow the Jupiter static imports and\nkeep resolving to the JUnit 4 overloads, producing code that fails\nto compile.\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Fix `JUnit4to5Migration` for classes that extend `org.junit.Assert` (…",
          "author_name": "Craig P. Motlin",
          "author_login": "motlin",
          "committed_at": "2026-05-15T17:09:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9f8b6e7e985244211dbe568e1e7adada55f98f3c",
          "body": "* OpenRewrite recipe best practices\n\nUse this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>\n\n* Update JUnit6BestPracticesTest.java\n\n---------\n\nCo-authored-by: Moderne <team@moderne.io>\nCo-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices (#997)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-15T11:12:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "10384e5055c45ccfcebfe6a30f0e602e93fe2870",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-15T10:53:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ddd9959fb52ec6c90eedbfe6ef9a1349fc72ab39",
          "body": null,
          "is_bot": false,
          "headline": "Cleanup junit vintage engine (#993)",
          "author_name": "sullis",
          "author_login": "sullis",
          "committed_at": "2026-05-08T12:45:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97d3a00b406f21a67bb8cbc3e8676120774ca1c3",
          "body": null,
          "is_bot": false,
          "headline": "Add unit test for JUnit6BestPractices (#994)",
          "author_name": "sullis",
          "author_login": "sullis",
          "committed_at": "2026-05-08T06:38:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "94343228a8189e274aefc975c2791a100049a6cd",
          "body": "…parisonToAssertEquals` (#995)\n\n`UseAssertSame` was rewriting `assertTrue(wrapper == primitive)` to\n`assertSame(wrapper, primitive)`, which fails: Java unboxes for `==`,\nbut `assertSame` autoboxes the primitive into a new wrapper and\ncompares by reference. Closes the gap left by #992 (primitive-primitive\nonly) by also handling the mixed wrapper-primitive form, with\n`AssertTrueComparisonToAssertEquals` extended to accept it.",
          "is_bot": false,
          "headline": "Skip wrapper-primitive in `UseAssertSame`; rewrite via `AssertTrueCom…",
          "author_name": "Craig P. Motlin",
          "author_login": "motlin",
          "committed_at": "2026-05-08T06:34:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "22b9122976eff23c533fada6bdf834cb68092991",
          "body": "Converting `assertTrue(a == b)` to `assertSame(a, b)` changes value\nequality into reference equality on the boxed types, which fails for\nmixed primitive types like `123L == 123`.",
          "is_bot": false,
          "headline": "Skip primitive comparisons in `UseAssertSame` (#992)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-05T22:35:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9104c055bd48e2e158533b35392bf85acabb9aa6",
          "body": "* Support `textBlock` attribute in `CsvSourceToValueSource`\n\nResolves #985\n\n* Skip textBlock conversion when values contain single quotes\n\nSingle quote is CsvSource's default quote character, so converting\n'apple' to a ValueSource string literal would change the parsed value.\n\n* Use `StringUtils.con\n[…]\nnsWhitespace` and inline trivial checks\n\nReplaces three local helpers with `StringUtils.containsWhitespace` for\nthe whitespace check and inline `String#indexOf` calls for the single-\ncharacter checks.",
          "is_bot": false,
          "headline": "Support `textBlock` attribute in `CsvSourceToValueSource` (#989)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-05T09:48:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b23a699aea34d008c13ca43f50362a652c2b12c6",
          "body": "…` (#990)\n\nRemoving an explicit `openMocks(this)` call shifts `@Mock` fields onto\nthe extension/runner's strict mocking session, which may surface\n`UnnecessaryStubbingException` for tests that previously benefited from\nthe lenient mocks created by `openMocks`. Mention the opt-out via\n`@MockitoSettings(strictness = Strictness.LENIENT)`.\n\nRefs #987",
          "is_bot": false,
          "headline": "Document strictness implication of `RemoveInitMocksIfRunnersSpecified…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-04T21:08:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b06f6a4d6aa4a362a30b58784b79c797f8e3cf4d",
          "body": "The captor registers a matcher used to capture values during later\nreal invocations; removing the stubbing breaks the capture.",
          "is_bot": false,
          "headline": "Preserve `doNothing()` stubbings using `ArgumentCaptor.capture()` (#988)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-04T18:51:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "41ace45e65e1766cd71de8f81b9afb8b186b356e",
          "body": "…...\")` (#983)\n\n* Guard PrepareForTest fullyQualifiedNames cast against ClassCastException\n\nAdd an `instanceof J.NewArray` check before casting the assignment value\nin `extractPrepareForTestClasses`, so `@PrepareForTest(fullyQualifiedNames = \"...\")`\nwith a string literal no longer crashes the recipe\n[…]\nsages such as\n`@PrepareForTest(fullyQualifiedNames = \"...\")` or\n`@PrepareForTest(fullyQualifiedNames = {\"a\",\"b\"})` leave the source intact\ninstead of stripping an annotation the recipe cannot migrate.",
          "is_bot": false,
          "headline": "Fix `ClassCastException` for `@PrepareForTest(fullyQualifiedNames = \"…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-01T16:33:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "67482655a174b463becab942387ddfadeca8cb8d",
          "body": "Switch the JavaTemplate to take `Class<?>` directly and construct a\nproper class literal so the resulting `assertInstanceOf` invocation\ncarries type info, allowing `maybeAddImport` to add the missing static\nimport.",
          "is_bot": false,
          "headline": "Fix AssertTrueInstanceofToAssertInstanceOf static import (#984)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-05-01T11:36:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f3a0fb90305c123e0a1bf4747b459c64ea6207a8",
          "body": "Restrict to files that actually import PowerMockito, so plain\nMockito-only tests no longer run through PowerMockito conversion logic.\n\nFixes #980",
          "is_bot": false,
          "headline": "Tighten PowerMockitoMockStaticToMockito precondition (#981)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-29T22:18:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a298e6b4233e625a341a603796c6af8a1aed9b1b",
          "body": "* Upgrade junit-bom to 6.x in JUnit 5 to 6 migration\n\nThe JUnit5to6Migration recipe upgraded individual junit-jupiter\nartifacts but left the org.junit:junit-bom import managed in\ndependencyManagement on 5.x, including when its version was\nreferenced through a property.\n\n* Use recipeFromResources in JUnit5to6MigrationTest\n\n* Return assertion actual() instead of explicit return",
          "is_bot": false,
          "headline": "Upgrade junit-bom to 6.x in JUnit 5 to 6 migration (#978)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-29T21:47:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c1bf1c1b5a3f08478325a6f38fc40a4390c04529",
          "body": "The \"remove bare mockStatic\" guard didn't recognize J.Return as a valid\nancestor, so mockStatic(X.class) returned from a helper was nullified —\ncollapsing the return statement to `return;` and breaking compilation\nagainst the helper's MockedStatic<X> return type.",
          "is_bot": false,
          "headline": "Preserve mockStatic when returned from helper methods (#979)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-29T21:45:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9bd6b9f7aa615f8c4dbd1712ecfae18ac48e73fc",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-29T09:59:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "56f133a6de462b1222ac3acc1f4e8339e32f3137",
          "body": null,
          "is_bot": false,
          "headline": "Cleanup Mockito AutoCloseable fields (#977)",
          "author_name": "sullis",
          "author_login": "sullis",
          "committed_at": "2026-04-28T21:10:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "de80c8f5a402b909bdce4f49f1fbaa38c7943179",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-28T10:20:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "130258985b4dba189cdf403961d4a72404965a3f",
          "body": "Use this link to re-run the recipe: https://dev.moderne.io/recipes/org.openrewrite.java.recipes.DeclarativeSingleton?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl#defaults=W3sidmFsdWUiOltdLCJuYW1lIjoiZXhjbHVzaW9ucyJ9XQ==\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Make declarative recipes singletons",
          "author_name": "Sam Snyder",
          "author_login": "sambsnyd",
          "committed_at": "2026-04-24T20:37:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "92de89a921d1db7934afad066065ce6083ea6176",
          "body": "The recipe asked for `okhttp-4.10` via `classpathFromResources`, but\nthat artifact is only bundled in the test type table — production runs\nof the recipe (e.g. via the Moderne CLI) fail with\n`IllegalArgumentException: Unable to find classpath resource\ndependencies beginning with: 'okhttp-4.10'` when\n[…]\nalready lenient about\nunresolved members), so dropping the unused `okhttp-4.10` request is\nsufficient and matches the sister `UpdateMockWebServerMockResponse`\nrecipe, which uses just `mockwebserver3`.",
          "is_bot": false,
          "headline": "Fix UpdateMockWebServerDispatcher classpath lookup (#975)",
          "author_name": "Knut Wannheden",
          "author_login": "knutwannheden",
          "committed_at": "2026-04-23T06:18:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "64260b8db6a8169dd3c655c523fb40ff538ce3e4",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.ExamplesExtractor?organizationId=QUxML09wZW4gU291cmNlL09wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "Extract documentation examples from tests",
          "author_name": "Greg Oledzki",
          "author_login": "greg-at-moderne",
          "committed_at": "2026-04-21T07:05:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e09430b9f7f8146ae5917a97135b70359982972e",
          "body": "…ion (#966)\n\n* Add @MockitoSettings(strictness = Strictness.WARN) support for Mockito migration\n\nAddresses the UnnecessaryStubbingException issue that occurs after migrating\nfrom Mockito 1.x, where unused stubbings were silently ignored but now fail\nunder MockitoExtension's default STRICT_STUBS stri\n[…]\nindAnnotations check, move maybeAddImport calls next to their\ntemplates, and use a static import for Comparator.comparing.\n\n* Use direct return\n\n---------\n\nCo-authored-by: Tim te Beek <tim@moderne.io>",
          "is_bot": false,
          "headline": "Add @MockitoSettings(strictness = Strictness.WARN) for Mockito migrat…",
          "author_name": "Benjamin Muschko",
          "author_login": "bmuschko",
          "committed_at": "2026-04-18T10:32:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f041383697177b175a94e1fcf51e25c425253414",
          "body": "…(#972)\n\n* Fix MockWebServer Dispatcher return type and RecordedRequest renames\n\nRestore the `MockResponse` return type on `Dispatcher.dispatch()`\noverrides after the blanket `MockResponse` → `MockResponse.Builder`\nrename, wrapping return expressions with `.build()`. Also rename\n`RecordedRequest.get\n[…]\nch delegates type attribution to the parser and removes the\nTree/Collections imports.\n\n* Regenerate recipes.csv for UpdateMockWebServerDispatcher\n\n* Use MethodMatcher for dispatch() override detection",
          "is_bot": false,
          "headline": "Fix MockWebServer Dispatcher return type and RecordedRequest renames …",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-17T16:40:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "13f0a17295a7c3d8921eafa766534738b5f2f2e5",
          "body": "Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.rewrite.OpenRewriteRecipeBestPractices?organizationId=QUxML01vZGVybmUvTW9kZXJuZSArIE9wZW5SZXdyaXRl\n\nCo-authored-by: Moderne <team@moderne.io>",
          "is_bot": false,
          "headline": "OpenRewrite recipe best practices",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-17T11:01:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "83d1f44e611143ee6d1b9bec6aa7a33820ea04bd",
          "body": "…971)\n\nFollow-up to #970, which disabled `doNotChangeAlreadyUnitTestMethods` with\ncomment \"flaky on CI but I don't know why\".\n\nRoot cause: as of rewrite 8.79.4 (openrewrite/rewrite#7364), the Kotlin\nparser maps non-nullable `kotlin.Unit` to JVM `JavaType.Primitive.Void`.\n`TypeUtils.isOfClassType(typ\n[…]\ne was CI-only.\n\nUse `KotlinTypeUtils.isKotlinUnit` (introduced in the same PR #7364)\nwhich accepts either `JavaType.Primitive.Void` or a `kotlin.Unit` class\nreference, and re-enable the disabled test.",
          "is_bot": false,
          "headline": "Use KotlinTypeUtils.isKotlinUnit to match Kotlin void return types (#…",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-16T21:59:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "675b57d5da25e6a02ab84231be124c02a6c8888c",
          "body": "…da expressions (#970)\n\n* Fix RemoveTryCatchFailBlocks failing when removing blocks within lambda expressions\n\n* Maybe this will fix test failure seen on CI\n\n* Disable the test because I don't care enough to investigate why it's failing on CI but not locally",
          "is_bot": false,
          "headline": "Fix RemoveTryCatchFailBlocks failing when removing blocks within lamb…",
          "author_name": "Sam Snyder",
          "author_login": "sambsnyd",
          "committed_at": "2026-04-15T22:15:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea724781c80db6ee42eab3443689f1c8e154d893",
          "body": "* Fix ClassCastException in RemoveTimesZeroAndOne when verify arg is not a method invocation (#968)\n\n* Add @Issue annotation to retainVerificationModeIdentifier test",
          "is_bot": false,
          "headline": "Fix ClassCastException in RemoveTimesZeroAndOne (#969)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-13T10:52:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "58bcc123e91b2aaef3d5f36a0ba728f8e6586f1c",
          "body": "* Add MigrateToKafkaNative recipe for Testcontainers (#568)\n\n* Address review feedback on ReplaceContainerImageName\n\n- Use @RequiredArgsConstructor with @Getter final fields for displayName/description\n- Make MethodMatcher a static field\n- Use UsesMethod precondition instead of UsesType\n- Also handl\n[…]\nalents:\n- yandex/clickhouse-server -> clickhouse/clickhouse-server\n- shopify/toxiproxy -> ghcr.io/shopify/toxiproxy\n- vault -> hashicorp/vault\n\nWhen newImage has no tag, the original tag is preserved.",
          "is_bot": false,
          "headline": "Add MigrateToKafkaNative recipe for Testcontainers (#568) (#967)",
          "author_name": "Tim te Beek",
          "author_login": "timtebeek",
          "committed_at": "2026-04-11T21:32:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 100,
      "commits_last_year": 314,
      "latest_release_at": "2026-07-20T14:04:34Z",
      "latest_release_tag": "v3.42.1",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 52,
      "days_since_latest_release": 5,
      "mean_days_between_releases": 9.4
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 62,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": []
    },
    "popularity": {
      "forks": 104,
      "stars": 99,
      "watchers": 7,
      "fork_history": {
        "days": [
          {
            "date": "2020-12-02",
            "count": 1
          },
          {
            "date": "2021-05-19",
            "count": 1
          },
          {
            "date": "2021-06-15",
            "count": 1
          },
          {
            "date": "2021-07-28",
            "count": 1
          },
          {
            "date": "2021-08-08",
            "count": 1
          },
          {
            "date": "2021-08-20",
            "count": 1
          },
          {
            "date": "2022-02-10",
            "count": 1
          },
          {
            "date": "2022-02-19",
            "count": 1
          },
          {
            "date": "2022-05-30",
            "count": 1
          },
          {
            "date": "2022-06-06",
            "count": 1
          },
          {
            "date": "2022-11-22",
            "count": 1
          },
          {
            "date": "2022-11-27",
            "count": 1
          },
          {
            "date": "2023-01-18",
            "count": 1
          },
          {
            "date": "2023-01-26",
            "count": 1
          },
          {
            "date": "2023-01-31",
            "count": 1
          },
          {
            "date": "2023-03-22",
            "count": 1
          },
          {
            "date": "2023-04-12",
            "count": 1
          },
          {
            "date": "2023-04-15",
            "count": 1
          },
          {
            "date": "2023-04-28",
            "count": 1
          },
          {
            "date": "2023-05-05",
            "count": 1
          },
          {
            "date": "2023-05-13",
            "count": 1
          },
          {
            "date": "2023-05-16",
            "count": 1
          },
          {
            "date": "2023-05-17",
            "count": 1
          },
          {
            "date": "2023-05-18",
            "count": 1
          },
          {
            "date": "2023-05-23",
            "count": 1
          },
          {
            "date": "2023-07-05",
            "count": 1
          },
          {
            "date": "2023-07-19",
            "count": 1
          },
          {
            "date": "2023-07-29",
            "count": 1
          },
          {
            "date": "2023-08-01",
            "count": 1
          },
          {
            "date": "2023-08-02",
            "count": 1
          },
          {
            "date": "2023-09-08",
            "count": 1
          },
          {
            "date": "2023-10-01",
            "count": 1
          },
          {
            "date": "2023-10-13",
            "count": 1
          },
          {
            "date": "2023-10-18",
            "count": 1
          },
          {
            "date": "2023-11-09",
            "count": 1
          },
          {
            "date": "2023-11-22",
            "count": 1
          },
          {
            "date": "2023-12-18",
            "count": 1
          },
          {
            "date": "2024-01-08",
            "count": 1
          },
          {
            "date": "2024-01-25",
            "count": 1
          },
          {
            "date": "2024-01-31",
            "count": 1
          },
          {
            "date": "2024-02-18",
            "count": 1
          },
          {
            "date": "2024-04-12",
            "count": 1
          },
          {
            "date": "2024-05-16",
            "count": 1
          },
          {
            "date": "2024-05-20",
            "count": 1
          },
          {
            "date": "2024-06-04",
            "count": 1
          },
          {
            "date": "2024-06-07",
            "count": 1
          },
          {
            "date": "2024-06-15",
            "count": 1
          },
          {
            "date": "2024-06-19",
            "count": 1
          },
          {
            "date": "2024-06-25",
            "count": 1
          },
          {
            "date": "2024-07-03",
            "count": 1
          },
          {
            "date": "2024-07-08",
            "count": 1
          },
          {
            "date": "2024-07-23",
            "count": 1
          },
          {
            "date": "2024-08-13",
            "count": 1
          },
          {
            "date": "2024-08-14",
            "count": 1
          },
          {
            "date": "2024-09-06",
            "count": 1
          },
          {
            "date": "2024-09-07",
            "count": 1
          },
          {
            "date": "2024-09-09",
            "count": 1
          },
          {
            "date": "2024-09-11",
            "count": 1
          },
          {
            "date": "2024-09-16",
            "count": 1
          },
          {
            "date": "2024-10-10",
            "count": 1
          },
          {
            "date": "2024-10-15",
            "count": 1
          },
          {
            "date": "2024-10-29",
            "count": 1
          },
          {
            "date": "2024-10-31",
            "count": 1
          },
          {
            "date": "2024-11-05",
            "count": 1
          },
          {
            "date": "2024-11-17",
            "count": 1
          },
          {
            "date": "2024-12-04",
            "count": 1
          },
          {
            "date": "2024-12-06",
            "count": 1
          },
          {
            "date": "2024-12-20",
            "count": 2
          },
          {
            "date": "2024-12-27",
            "count": 1
          },
          {
            "date": "2025-01-13",
            "count": 1
          },
          {
            "date": "2025-01-26",
            "count": 1
          },
          {
            "date": "2025-02-11",
            "count": 1
          },
          {
            "date": "2025-04-10",
            "count": 1
          },
          {
            "date": "2025-04-18",
            "count": 1
          },
          {
            "date": "2025-04-19",
            "count": 1
          },
          {
            "date": "2025-06-06",
            "count": 1
          },
          {
            "date": "2025-06-10",
            "count": 1
          },
          {
            "date": "2025-07-13",
            "count": 1
          },
          {
            "date": "2025-08-13",
            "count": 1
          },
          {
            "date": "2025-08-20",
            "count": 1
          },
          {
            "date": "2025-09-09",
            "count": 1
          },
          {
            "date": "2025-09-24",
            "count": 1
          },
          {
            "date": "2025-10-16",
            "count": 1
          },
          {
            "date": "2025-10-26",
            "count": 1
          },
          {
            "date": "2025-10-27",
            "count": 1
          },
          {
            "date": "2025-11-06",
            "count": 1
          },
          {
            "date": "2025-11-18",
            "count": 1
          },
          {
            "date": "2025-11-24",
            "count": 1
          },
          {
            "date": "2025-12-07",
            "count": 1
          },
          {
            "date": "2026-01-04",
            "count": 1
          },
          {
            "date": "2026-01-07",
            "count": 1
          },
          {
            "date": "2026-01-18",
            "count": 1
          },
          {
            "date": "2026-01-27",
            "count": 1
          },
          {
            "date": "2026-02-11",
            "count": 1
          },
          {
            "date": "2026-03-23",
            "count": 1
          },
          {
            "date": "2026-05-28",
            "count": 1
          },
          {
            "date": "2026-06-19",
            "count": 1
          },
          {
            "date": "2026-07-10",
            "count": 1
          },
          {
            "date": "2026-07-11",
            "count": 1
          },
          {
            "date": "2026-07-14",
            "count": 1
          },
          {
            "date": "2026-07-25",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 102,
        "total_forks": 104
      },
      "star_history": null,
      "open_issues_and_prs": 23
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "build.gradle.kts"
      ],
      "largest_source_bytes": 73880,
      "source_files_sampled": 402,
      "oversized_source_files": 3,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "build.gradle.kts"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "maven"
      ],
      "dependencies": [],
      "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": 0,
        "merged_prs": 603,
        "open_issues": 23,
        "closed_ratio": 0.94,
        "closed_issues": 361,
        "closed_unmerged_prs": 68
      },
      "bus_factor": 3,
      "bot_contributors": 2,
      "top_contributors": [
        {
          "type": "User",
          "login": "timtebeek",
          "commits": 431,
          "avatar_url": "https://avatars.githubusercontent.com/u/1027334?v=4"
        },
        {
          "type": "User",
          "login": "pway99",
          "commits": 138,
          "avatar_url": "https://avatars.githubusercontent.com/u/22326988?v=4"
        },
        {
          "type": "User",
          "login": "jkschneider",
          "commits": 125,
          "avatar_url": "https://avatars.githubusercontent.com/u/1697736?v=4"
        },
        {
          "type": "User",
          "login": "sambsnyd",
          "commits": 121,
          "avatar_url": "https://avatars.githubusercontent.com/u/8549033?v=4"
        },
        {
          "type": "User",
          "login": "aegershman",
          "commits": 83,
          "avatar_url": "https://avatars.githubusercontent.com/u/5619476?v=4"
        },
        {
          "type": "User",
          "login": "knutwannheden",
          "commits": 61,
          "avatar_url": "https://avatars.githubusercontent.com/u/408765?v=4"
        },
        {
          "type": "User",
          "login": "tkvangorder",
          "commits": 32,
          "avatar_url": "https://avatars.githubusercontent.com/u/1878529?v=4"
        },
        {
          "type": "User",
          "login": "amishra-u",
          "commits": 20,
          "avatar_url": "https://avatars.githubusercontent.com/u/119983081?v=4"
        },
        {
          "type": "User",
          "login": "steve-aom-elliott",
          "commits": 20,
          "avatar_url": "https://avatars.githubusercontent.com/u/7945003?v=4"
        },
        {
          "type": "User",
          "login": "MBoegers",
          "commits": 17,
          "avatar_url": "https://avatars.githubusercontent.com/u/7557397?v=4"
        }
      ],
      "contributors_sampled": 98,
      "top_contributor_share": 0.318
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "publish.yml",
        "repository-backup.yml",
        "stale.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [],
      "has_editorconfig": true,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 9,
            "reason": "binaries present in source code",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 9,
            "reason": "22 out of 23 merged PRs checked by a CI test -- score normalized to 9",
            "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": 2,
            "reason": "Found 8/30 approved changesets -- score normalized to 2",
            "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": 9,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 12 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": null,
            "reason": "packaging workflow not 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": "c9ebc4f47b33d470bd739fa9930fd391df565112",
        "ran_at": "2026-07-25T17:21:02Z",
        "aggregate_score": 5.2,
        "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-25T07:28:02Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-07-25T07:19:00Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 91,
          "created_at": "2021-03-24T19:08:03Z",
          "last_comment_at": "2024-06-22T13:47:54Z",
          "last_comment_author": "MBoegers"
        },
        {
          "number": 195,
          "created_at": "2022-03-28T18:40:55Z",
          "last_comment_at": "2022-04-15T21:14:45Z",
          "last_comment_author": "pway99"
        },
        {
          "number": 196,
          "created_at": "2022-03-28T18:47:53Z",
          "last_comment_at": "2022-03-28T21:36:03Z",
          "last_comment_author": "pway99"
        },
        {
          "number": 197,
          "created_at": "2022-03-28T19:59:17Z",
          "last_comment_at": "2022-03-28T21:48:00Z",
          "last_comment_author": "pway99"
        },
        {
          "number": 203,
          "created_at": "2022-03-30T15:44:34Z",
          "last_comment_at": "2022-03-30T20:04:42Z",
          "last_comment_author": "tkvangorder"
        },
        {
          "number": 221,
          "created_at": "2022-04-18T14:42:36Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 224,
          "created_at": "2022-04-27T16:15:19Z",
          "last_comment_at": "2025-09-02T13:54:46Z",
          "last_comment_author": "timtebeek"
        },
        {
          "number": 225,
          "created_at": "2022-04-27T16:16:46Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 231,
          "created_at": "2022-06-06T18:22:00Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 278,
          "created_at": "2022-10-27T20:51:53Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 318,
          "created_at": "2023-02-23T03:13:25Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 438,
          "created_at": "2023-12-05T11:52:15Z",
          "last_comment_at": "2023-12-05T11:53:38Z",
          "last_comment_author": "timtebeek"
        },
        {
          "number": 453,
          "created_at": "2024-01-04T15:50:35Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 458,
          "created_at": "2024-01-15T20:01:01Z",
          "last_comment_at": "2024-08-20T09:01:16Z",
          "last_comment_author": "timtebeek"
        },
        {
          "number": 508,
          "created_at": "2024-04-17T11:10:59Z",
          "last_comment_at": "2024-04-30T22:34:23Z",
          "last_comment_author": "kernanj"
        },
        {
          "number": 522,
          "created_at": "2024-06-13T10:49:53Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 529,
          "created_at": "2024-06-17T13:20:51Z",
          "last_comment_at": "2024-06-26T09:27:58Z",
          "last_comment_author": "timtebeek"
        },
        {
          "number": 697,
          "created_at": "2025-04-09T16:43:07Z",
          "last_comment_at": "2025-05-04T11:23:27Z",
          "last_comment_author": "timtebeek"
        },
        {
          "number": 723,
          "created_at": "2025-05-07T09:24:06Z",
          "last_comment_at": "2025-05-09T06:25:57Z",
          "last_comment_author": "stefan-schilling"
        },
        {
          "number": 738,
          "created_at": "2025-05-24T19:55:24Z",
          "last_comment_at": "2025-05-28T22:38:21Z",
          "last_comment_author": "timtebeek"
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/openrewrite/rewrite-testing-frameworks",
    "host": "github.com",
    "name": "rewrite-testing-frameworks",
    "owner": "openrewrite"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 72,
      "inputs": {
        "security": 52,
        "vitality": 100,
        "community": 61,
        "governance": 77,
        "engineering": 63
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 100,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "excellent",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "commits_last_year": 314,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 52
            },
            "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": "52/52 weeks with commits",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 52
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "314 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 314
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 12 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": 100,
              "latest_release_tag": "v3.42.1",
              "releases_from_tags": false,
              "days_since_latest_release": 5,
              "mean_days_between_releases": 9.4
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "100 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 100
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 5 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~9.4 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 9.4
                    }
                  }
                ],
                "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": 0,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 0 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "moderate",
        "name": "Community & Adoption",
        "value": 61,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "moderate",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 53,
            "inputs": {
              "forks": 104,
              "stars": 99,
              "watchers": 7,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "99 stars",
                "points": 32.3,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 99
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "104 forks",
                "points": 16.8,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 104
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "7 watchers",
                "points": 4.3,
                "status": "partial",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "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": "license file present, not a recognized license",
                "points": 16.9,
                "status": "partial",
                "details": [
                  {
                    "code": "license_custom",
                    "params": {}
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 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": 77,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "good",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "bus_factor": 3,
              "contributors_sampled": 98,
              "top_contributor_share": 0.318
            },
            "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 32% of commits",
                "points": 15.3,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 32
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "98 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 98
                    }
                  }
                ],
                "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": 81,
            "inputs": {
              "merged_prs": 603,
              "open_issues": 23,
              "closed_issues": 361,
              "issue_closed_ratio": 0.94,
              "closed_unmerged_prs": 68
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "94% of issues closed",
                "points": 43.9,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 94
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "603/671 decided PRs merged",
                "points": 34.4,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 603,
                      "decided": 671
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 8/30 approved changesets -- score normalized to 2",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "good",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 76,
            "inputs": {
              "followers": 984,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "openrewrite",
              "public_repos": 75,
              "account_age_days": 2341
            },
            "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": "984 followers of openrewrite",
                "points": 21.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 984,
                      "login": "openrewrite"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "75 public repos, account ~6 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 75
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 6
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "moderate",
        "name": "Engineering Quality",
        "value": 63,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": true,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "4 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 6.4,
                "status": "met",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "22 out of 23 merged PRs checked by a CI test -- score normalized to 9",
                "points": 18,
                "status": "partial",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "topics": [
                "openrewrite",
                "openrewrite-recipes"
              ],
              "has_wiki": false,
              "homepage": null,
              "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "2 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 52,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 52,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 5.2
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "binaries present in source code",
                "points": 6.8,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "22 out of 23 merged PRs checked by a CI test -- score normalized to 9",
                "points": 2.2,
                "status": "partial",
                "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 8/30 approved changesets -- score normalized to 2",
                "points": 1.5,
                "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.2,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 12 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 not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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": "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": 14
            },
            "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": 58,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.9,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "90 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 90,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [],
              "agent_commit_share": 0.02,
              "toolchain_manifests": [
                "build.gradle.kts"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "build.gradle.kts (toolchain convention, no task runner)",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "toolchain_convention",
                    "params": {
                      "files": "build.gradle.kts"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Java (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Java"
                    }
                  }
                ],
                "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": "2 of the last 100 commits agent-authored or agent-credited",
                "points": 4,
                "status": "partial",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 2,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "dependency automation configured, none observed in the sampled commits",
                "points": 5,
                "status": "partial",
                "details": [
                  {
                    "code": "dependency_bot_config_only",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "primary_language": "Java",
              "largest_source_bytes": 73880,
              "source_files_sampled": 402,
              "oversized_source_files": 3
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Java (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Java"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "3/402 source files over 60KB",
                "points": 54.6,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 402,
                      "oversized": 3
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-25T17:21:27.863955Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/o/openrewrite/rewrite-testing-frameworks.svg",
  "full_name": "openrewrite/rewrite-testing-frameworks",
  "license_state": "custom",
  "license_spdx": null
}

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

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

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