Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-27 15:59 UTC

akankov / html-min

HTML Compressor and Minifier for PHP 8.3+ (maintained fork of voku/HtmlMin)

HTML · PHPMIT★ 2 estrellas⑂ 0 forksdesde abr 2026Ver en GitHub ↗

akankov/html-min tiene un índice de salud de 66 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Vitality (83/100) y la más baja, Sustainability & Governance (50/100). Se actualizó por última vez hoy. Una sola persona concentra la mayor parte del trabajo reciente.

66
global / 100
Moderado

Índice de salud del software

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

66
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

Aleksei KankovCuenta personal
0 seguidores15 repositorios públicosdesde jul 2012

Este repositorio pertenece a una cuenta personal. Un proyecto con un único propietario conlleva más riesgo de continuidad que uno respaldado por una organización.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicaciónEtiquetas
Packagistakankov/html-minv2.11.069.78821hace 37 díascompressionhtmlminifyminifiercompress

Métricas por categoría

Vitalidad

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

83Bueno · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
7.6/36Cadencia de commits — 11/52 semanas con commits
18/18Volumen de commits — 203 commits en el último año
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Datos de entrada utilizados
commits_last_year203
human_commit_share0,94
days_since_last_push0
active_weeks_last_year11
Cómo se puntúa
27/27Publica versiones — 19 versiones publicadas
36/36Recencia de las versiones — última versión hace 37 días
27/27Cadencia de publicación — una versión cada ~4,8 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count19
latest_release_tagv2.11.0
releases_from_tagsno
days_since_latest_release37
mean_days_between_releases4,8
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?

52Moderado · 18% del índice global
Cómo se puntúa
0/60Estrellas — 2 estrellas
0/25Forks — 0 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks0
stars2
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (MIT)
18/18Guía CONTRIBUTING
13.5/13.5Código de conducta
7.2/7.2Plantilla de issues
6.3/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributing
has_issue_template
has_code_of_conduct
has_pull_request_template
Cómo se puntúa
64.6/80Descargas mensuales — 69.788 descargas/mes en packagist
3.2/20Dependientes en el registro — 2 paquetes dependen de él
Datos de entrada utilizados
packagesakankov/html-min
dependents2
ecosystemspackagist
total_downloads204.069
monthly_downloads69.788

Sostenibilidad y Gobernanza

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

50Moderado · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
0/46.8Resolución de issues — sin issues o sin datos
38.2/38.3Aceptación de PR — 60/60 PR decididos fusionados
0/15OpenSSF Scorecard: Code-Review — Found 0/11 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs60
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues. Los pesos restantes se han renormalizado.
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
0/25Alcance del propietario — 0 seguidores de akankov
20.8/25Trayectoria — 15 repos públicos, cuenta de ~14 años
Datos de entrada utilizados
followers0
owner_typeUser
is_verified
owner_loginakankov
public_repos15
account_age_days5126
Excluidos de la puntuación (sin datos o no aplicable): Dominio verificado. Los pesos restantes se han renormalizado.
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en packagist
35/35Recencia de publicación — última publicación hace 37 días
20/20Historial de versiones — 21 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesakankov/html-min
ecosystemspackagist
any_deprecatedno
min_days_since_publish37

Calidad de Ingeniería

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

80Bueno · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 1 flujo(s) de trabajo
24/24Pruebas presentes
16/16Configuración de linter — .php-cs-fixer.dist.php, phpstan.neon
0/9.6Hooks de pre-commit
6.4/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 17 out of 17 merged PRs checked by a CI test -- score normalized to 10
Datos de entrada utilizados
has_ci
has_tests
has_editorconfig
has_linter_config
has_precommit_configno

Documentación

65Moderado
Cómo se puntúa
30/30README
0/25Directorio de documentación
15/15Sitio de documentación / página del proyecto — https://github.com/akankov/html-min
10/10Descripción del repositorio
0/10Topics
10/10Wiki
Datos de entrada utilizados
topics
has_wiki
homepagehttps://github.com/akankov/html-min
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?

64Moderado · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
2.5/2.5CI-Tests — 17 out of 17 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/11 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
0/10Dangerous-Workflow — sin datos
7.5/7.5Dependency-Update-Tool — update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Licencia — license file detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — sin datos
0/5Pinned-Dependencies — sin datos
5/5SAST — SAST tool is run on all commits
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — sin datos
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated13
scorecard_versionv5.5.0
checks_inconclusive5
scorecard_aggregate6,4
Excluidos de la puntuación (sin datos o no aplicable): dangerous_workflow, packaging, pinned_dependencies, signed_releases, token_permissions. 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.

55Moderado · 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 — 94 de 94 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share1
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
18/18Arranque con un solo comando — Makefile
22/22Pruebas automatizadas
11/11Configuración de lint / formato — .php-cs-fixer.dist.php, phpstan.neon
0/11Verificación estática de tipos
0/10Entorno reproducible
0/10Práctica demostrada con agentes — ningún commit con autoría de agente entre los últimos 100
8/8Mantenimiento automatizado — 6 de los últimos 100 commits son actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — sin datos
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_languageno
bootstrap_filesMakefile
has_devcontainerno
has_linter_config
typecheck_configs
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0,06
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Pinned-Dependencies. Los pesos restantes se han renormalizado.
Cómo se puntúa
0/45Código verificable por tipos — HTML sin configuración de verificación de tipos
55/55Tamaños de archivo manejables — 0/75 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languageHTML
largest_source_bytes34.986
source_files_sampled75
oversized_source_files0

Datos clave

2estrellas de GitHub
1contribuidores
203commits en los últimos 12 meses
0días desde el último push
19versiones publicadas
1factor bus
0issues abiertas
Packagistecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • No resolved dependencies carried a version and a supported ecosystem

Más detalle

OpenSSF Scorecard 6.4 / 10
6.4agregado

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-27 15:59 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests17 out of 17 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/11 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
n/dDangerous-Workflowno workflows found
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
n/dPackagingpackaging workflow not detected
n/dPinned-Dependenciesno dependencies found
10SASTSAST tool is run on all commits
10Security-Policysecurity policy file detected
n/dSigned-Releasesno releases found
n/dToken-PermissionsNo tokens found
10Vulnerabilities0 existing vulnerabilities detected
Dependencias directas 4
RegistroPaqueteRestricción de versiónManifiesto
Packagistpsr/http-factory^1.0composer.json
Packagistpsr/http-message^1.1 || ^2.0composer.json
Packagistpsr/http-server-middleware^1.0composer.json
Packagistpsr/log^3.0composer.json
Todas las dependencias 22

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

RegistroPaqueteVersiónRelación
Packagistpsr/http-factorydirecta
Packagistpsr/http-messagedirecta
Packagistpsr/http-server-middlewaredirecta
Packagistpsr/logdirecta
Packagistabordage/html-minindirecta
Packagistakankov/html-minindirecta
Packagistext-domindirecta
Packagistext-libxmlindirecta
Packagistext-mbstringindirecta
Packagistext-simplexmlindirecta
Packagistext-zlibindirecta
Packagistfriendsofphp/php-cs-fixerindirecta
Packagistinfection/infectionindirecta
Packagistnyholm/psr7indirecta
Packagistphan/phanindirecta
Packagistphpindirecta
Packagistphpbench/phpbenchindirecta
Packagistphpstan/phpstanindirecta
Packagistphpunit/phpunitindirecta
Packagistrector/rectorindirecta
Packagistvoku/html-minindirecta
Packagistzaininnari/html-minifierindirecta
Avisos de dependencias sin evaluar

El cotejo de avisos no pudo ejecutarse para este informe: No resolved dependencies carried a version and a supported ecosystem

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 716,
      "has_wiki": true,
      "homepage": "https://github.com/akankov/html-min",
      "languages": {
        "PHP": 423315,
        "HTML": 1344629,
        "Shell": 621,
        "Makefile": 7028,
        "Dockerfile": 411
      },
      "pushed_at": "2026-07-27T15:55:37Z",
      "created_at": "2026-04-17T08:24:06Z",
      "owner_type": "User",
      "updated_at": "2026-07-27T15:56:16Z",
      "description": "HTML Compressor and Minifier for PHP 8.3+ (maintained fork of voku/HtmlMin)",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "HTML",
      "significant_languages": [
        "HTML",
        "PHP"
      ]
    },
    "owner": {
      "blog": "https://kankov.me/",
      "name": "Aleksei Kankov",
      "type": "User",
      "login": "akankov",
      "company": null,
      "location": "Germany",
      "followers": 0,
      "avatar_url": "https://avatars.githubusercontent.com/u/1974569?v=4",
      "created_at": "2012-07-14T11:06:43Z",
      "is_verified": null,
      "public_repos": 15,
      "account_age_days": 5126
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v2.11.0",
          "kind": "minor",
          "published_at": "2026-06-19T18:00:08Z"
        },
        {
          "tag": "v2.9.0",
          "kind": "minor",
          "published_at": "2026-06-08T09:04:47Z"
        },
        {
          "tag": "v2.8.1",
          "kind": "patch",
          "published_at": "2026-06-07T17:38:39Z"
        },
        {
          "tag": "v2.8.0",
          "kind": "minor",
          "published_at": "2026-05-31T17:22:53Z"
        },
        {
          "tag": "v2.7.1",
          "kind": "patch",
          "published_at": "2026-05-31T15:38:46Z"
        },
        {
          "tag": "v2.7.0",
          "kind": "minor",
          "published_at": "2026-05-30T21:22:46Z"
        },
        {
          "tag": "v2.6.1",
          "kind": "patch",
          "published_at": "2026-05-29T15:33:00Z"
        },
        {
          "tag": "v2.6.0",
          "kind": "minor",
          "published_at": "2026-05-28T07:22:12Z"
        },
        {
          "tag": "v2.5.1",
          "kind": "patch",
          "published_at": "2026-05-13T07:10:21Z"
        },
        {
          "tag": "v2.5.0",
          "kind": "minor",
          "published_at": "2026-05-07T17:16:14Z"
        },
        {
          "tag": "v2.4.0",
          "kind": "minor",
          "published_at": "2026-05-07T16:33:50Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2026-05-07T07:02:29Z"
        },
        {
          "tag": "v2.2.0",
          "kind": "minor",
          "published_at": "2026-05-06T17:44:56Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2026-05-06T17:12:48Z"
        },
        {
          "tag": "v2.0.0",
          "kind": "major",
          "published_at": "2026-04-29T09:10:05Z"
        },
        {
          "tag": "v1.2.0",
          "kind": "minor",
          "published_at": "2026-04-25T10:53:20Z"
        },
        {
          "tag": "v1.1.0",
          "kind": "minor",
          "published_at": "2026-04-24T21:17:27Z"
        },
        {
          "tag": "v1.0.1",
          "kind": "patch",
          "published_at": "2026-04-24T19:40:53Z"
        },
        {
          "tag": "v1.0.0",
          "kind": "major",
          "published_at": "2026-04-17T15:54:01Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "a766e4039defbda13532d83bcf8cc7efc6a0c23f",
          "body": "…oup (#61)\n\nBumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).\n\n\nUpdates `actions/checkout` from 7.0.0 to 7.0.1\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Co\n[…]\npe: version-update:semver-patch\n  dependency-group: actions\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump actions/checkout from 7.0.0 to 7.0.1 in the actions gr…",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-07-27T15:55:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4e393df89012f7d0e51b988e03e8025af2a9ba8b",
          "body": "Bumps [actions/cache](https://github.com/actions/cache) from 5.0.5 to 6.1.0.\n- [Release notes](https://github.com/actions/cache/releases)\n- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)\n- [Commits](https://github.com/actions/cache/compare/v5.0.5...v6.1.0)\n\n---\nupdated-dependenc\n[…]\nirect:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump actions/cache from 5.0.5 to 6.1.0 (#60)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-07-06T06:58:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "efcc01b6dd8a7b7390219317eb3829432bbb0c0f",
          "body": "* bench: drop wyrihaximus/html-compress, bump voku/html-min to ^5.0\n\nwyrihaximus/html-compress is itself a wrapper around voku/html-min and its\nlatest stable (4.4.0) still pins voku ^4.5.1, which blocks the benchmark\nsuite from moving to voku 5.0. Since voku 5.0 is the current release we want\nto com\n[…]\n README Summary block. Dropped the wyrihaximus row; voku/html-min now\nbenchmarks at 5.0.0. Also fixed the hand-written competitor list in the\nREADME \"Benchmarks\" intro (outside the auto-synced block).",
          "is_bot": false,
          "headline": "bench: drop wyrihaximus/html-compress, benchmark voku/html-min 5.0 (#59)",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-30T08:12:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "74f099f50997e2261a81969b8365880716fc0ada",
          "body": "0.x major bump of the mutation-testing dev tool. require-dev only, so\ninvisible to consumers; composer.lock is untracked. Verified locally:\n`make infection` green on 0.34.0 (MSI floor 75 met). The only 0.34 CLI change\n(deprecated --filter flag) is not used by the make target.",
          "is_bot": false,
          "headline": "chore(deps-dev): bump infection/infection to ^0.34 (#58)",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-30T07:54:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c0496b22d9ec33b9e320a4c7fdc0516ca754c081",
          "body": "Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/v6.0.3...v7.0.0)\n\n---\nu\n[…]\nirect:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump actions/checkout from 6.0.3 to 7.0.0 (#57)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-29T08:07:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "079e960202fac9c801e5a2d3b7b5f879ff4a1fdd",
          "body": "Bumps the actions group with 1 update: [actions/cache](https://github.com/actions/cache).\n\n\nUpdates `actions/cache` from 5 to 5.0.5\n- [Release notes](https://github.com/actions/cache/releases)\n- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)\n- [Commits](https://github.com/action\n[…]\npe: version-update:semver-patch\n  dependency-group: actions\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump actions/cache from 5 to 5.0.5 in the actions group (#56)",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-29T08:06:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "239d51f37b36fc85d83ecd258a5c5bdf7002c0bd",
          "body": "…/codecov-action-7\n\nci(deps): bump codecov/codecov-action from 5 to 7",
          "is_bot": false,
          "headline": "Merge pull request #55 from akankov/dependabot/github_actions/codecov…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-19T21:11:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9ef865d7a77dfcd10907174409b76339e1a287a5",
          "body": "…-6a98abd9ac\n\nci(deps): bump actions/checkout from 6 to 6.0.3 in the actions group",
          "is_bot": false,
          "headline": "Merge pull request #54 from akankov/dependabot/github_actions/actions…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-19T21:11:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "95aefdad718c5216ee1daa436f312daabc1f515d",
          "body": "Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 7.\n- [Release notes](https://github.com/codecov/codecov-action/releases)\n- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/codecov/codecov-action/compare/v\n[…]\npendency-name: codecov/codecov-action\n  dependency-version: '7'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump codecov/codecov-action from 5 to 7",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-19T17:59:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f914c019ed21e23835f0366904e505919d83a178",
          "body": "Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).\n\n\nUpdates `actions/checkout` from 6 to 6.0.3\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://g\n[…]\nheckout\n  dependency-version: 6.0.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n  dependency-group: actions\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "ci(deps): bump actions/checkout from 6 to 6.0.3 in the actions group",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-06-19T17:59:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c4a7f8621f19468cbef5a302d6ce39d7ac097030",
          "body": "Release v2.11.0 — parser re-entrancy guard, keepBrokenHtml DoS cap, supply-chain tooling",
          "is_bot": false,
          "headline": "Merge pull request #53 from akankov/release/v2.11.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-19T17:58:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "11eaa6ab4ef24be5f21f75f4d0efbff3630bc508",
          "body": "…tooling\n\n- Re-entrancy guard on HtmlParser's shared static state: a nested minify()\n  (from an inline CSS/JS minifier callback or DOM observer) now throws a\n  LogicException instead of silently corrupting the placeholder round-trip;\n  the lock is released in a finally so a throwing callback cannot \n[…]\ncking\n  composer audit CI job.\n\nRelease v2.11.0.\n\nTests: tests/HtmlMinReentrancyTest.php, tests/HtmlMinKeepBrokenHtmlCapTest.php.\nLocal gates green: 406 phpunit tests, PHPStan level max, php-cs-fixer.",
          "is_bot": false,
          "headline": "feat: harden parser re-entrancy and keepBrokenHtml; add supply-chain …",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-19T17:44:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4518e116bab0146154c0d9449a2fa000d1ea9540",
          "body": "release: v2.10.0",
          "is_bot": false,
          "headline": "Merge pull request #52 from akankov/release/v2.10.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T08:39:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e381a3b71076f93b639f6d8220b5268c6348fbbf",
          "body": null,
          "is_bot": false,
          "headline": "docs(benchmarks): refresh latest.md for v2.10.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T08:33:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "276c337e81a2632113bcfa7bdef68fa398c8ebce",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.10.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T08:24:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4b34e5d9d27b39c25e8dda281a1a7c355b348d5b",
          "body": "docs(readme): CLI usage section + observer best-practices",
          "is_bot": false,
          "headline": "Merge pull request #51 from akankov/docs/cli-and-observer-guide",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T07:26:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d6d428c6e8be8b9affc442c974361f24c9bdb907",
          "body": "The vendor/bin/html-min CLI existed undocumented; the Extending section\nshowed how to attach an observer but nothing about phases, ordering\n(built-in OptimizeAttributes runs first in the After phase), per-element\ncost, exception propagation, or safe tree mutation. All claims verified\nagainst HtmlMin.php.",
          "is_bot": false,
          "headline": "docs(readme): CLI usage section and observer best-practices",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T07:22:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6e9a703e280e59986bdc39640bc008874bd8a62e",
          "body": "feat(omission): WHATWG spec audit + placeholder-nonce property tests",
          "is_bot": false,
          "headline": "Merge pull request #50 from akankov/test/nonce-and-omission-hardening",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T07:16:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8df482a08d0fb5ae0b71d47a50effc44227a8bba",
          "body": "…ed structural end tags\n\nAudit of OptionalTagOmission against WHATWG §13.1.2.4:\n\n- option/optgroup end tags now omit before <hr> (the <select> separator\n  addition); rt is handled at all (same rule as rp); p's followed-by list\n  gains the post-HTML4 blocks (details, dialog, figcaption, figure, main,\n[…]\ntion-before-hr case is built with explicit DOM nodes — libxml's\nHTML4-era parser relocates <hr> out of <select>, so a loadHTML-based case\nwould pass for the wrong reason and leave the hr arm untested.",
          "is_bot": false,
          "headline": "feat(omission): WHATWG spec audit — hr/rt/p-block gaps, comment-block…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T07:11:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8186cbac0db88c8f1a273f95146516a8ab230121",
          "body": "feat(config): MinifierOptions::aggressive() and ::conservative() presets",
          "is_bot": false,
          "headline": "Merge pull request #49 from akankov/feature/minifier-options-presets",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T07:09:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a060a5545dbb7a6613eb9af1dfd66d1621b67ae5",
          "body": "chore(bench): baseline refresh policy + staleness warning",
          "is_bot": false,
          "headline": "Merge pull request #48 from akankov/chore/bench-baseline-policy",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T06:43:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cc29951167e8c0c6c2786a155c37886a4f33b5d6",
          "body": "Two named starting points beside defaults(): aggressive() turns on every\nspec-valid byte saver (block-tag whitespace trimming, whitespace-only\ntext-node removal, html/head/body start-tag omission, spec-default\nattribute removal, inline CSS/JS minification) while deliberately leaving\nURL scheme strip\n[…]\n option can't silently join a preset; also pins the previously\nunpinned removeOmittedHtmlStartTags default and documents both presets and\nthe missing doRemoveOmittedHtmlStartTags setter in the README.",
          "is_bot": false,
          "headline": "feat(config): MinifierOptions::aggressive() and ::conservative() presets",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-12T06:42:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dd5b71c4cb0736c9d9511c639f8f363007662456",
          "body": "baseline.md (local, gitignored) is the release-diff snapshot, but nothing\nsaid when to refresh it — the last snapshot predated the last two bench\nruns. Document the policy (refresh on every minor release via 'make\nbench-baseline') in benchmarks/README.md and finish 'make bench' with a\nbench-baseline-check that warns when the snapshot is missing or >30 days\nolder than latest.md.",
          "is_bot": false,
          "headline": "chore(bench): baseline refresh policy + staleness warning",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-11T21:22:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9858f4bc758fcc4af7bffd95b62d48fe659055ce",
          "body": "fix(tests): stop Infection mutants dropping 0-byte min* files in the repo root",
          "is_bot": false,
          "headline": "Merge pull request #47 from akankov/fix/cli-test-mutant-droppings",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-11T21:18:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6a2ddf2542b2ff50fffa718bb0cbc609c1d33c72",
          "body": "…s in the repo root\n\nUnder Infection, the TrueValue mutator on the '--output=' str_starts_with()\ncheck turns an input path '/tmp/htmlminXYZ' into a CWD-relative 'minXYZ'\n(substr($arg, 9)), and the mutated writeOutput() then creates a 0-byte min*\nfile in whatever directory PHPUnit runs from — the rep\n[…]\nnore entry that papered over the symptom.\n\nReproduced and verified with: infection --filter=src/HtmlMin/Cli/Cli.php\n(leaked one file before, none after); full make infection stays above the\nMSI floor.",
          "is_bot": false,
          "headline": "fix(tests): run CliTest from a scratch dir so mutants can't drop file…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-11T17:19:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "52a6bad03648e3ef68c5a47681c4522bf593237e",
          "body": "Release v2.9.0",
          "is_bot": false,
          "headline": "Merge pull request #46 from akankov/release/v2.9.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T09:04:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1661a0802d8687788187454043e2eebe10fc8361",
          "body": null,
          "is_bot": false,
          "headline": "chore(release): v2.9.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:59:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2d7c3f4fc8f8dfddc5f48c8b31a2ce5ccfb011dc",
          "body": "Extract ProtectedContentManager from the HtmlMin god-class",
          "is_bot": false,
          "headline": "Merge pull request #45 from akankov/refactor/protected-content-manager",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:44:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d1d97068b699ef1eff55fa08b45c5aff427745b8",
          "body": "The comment/text-node handling moved to ProtectedContentManager, so those\nimports are no longer referenced in HtmlMin (CI php-cs-fixer no_unused_imports).",
          "is_bot": false,
          "headline": "style: drop now-unused DOMComment/DOMText imports from HtmlMin",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:36:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d475b837da2491f8a9998b927d6a70c2191e30c6",
          "body": "Move the protected-content machinery — <nocompress>/<code> protection, inline\n<script>/<style> swap-out (with optional inline minification), and conditional\n/ special comment handling — plus its state (the saved-node map, counter, and\nplaceholder token) out of HtmlMin into Internal\\ProtectedContentM\n[…]\nsuite green (374 tests), phpstan / cs / rector clean,\nand protected-content behaviours (nocompress kept, script untouched, conditional\ncomment preserved, plain comment removed) verified by smoke test.",
          "is_bot": false,
          "headline": "refactor(html-min): extract ProtectedContentManager from the god-class",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:29:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "960bdb90a7f686aa7368e9c5bc0687ede50f84db",
          "body": "Opt-in <html>/<head>/<body> start-tag omission",
          "is_bot": false,
          "headline": "Merge pull request #44 from akankov/feature/start-tag-omission",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:12:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "646f1d33016d44aca848fa7a11ed21ca09ed5b9b",
          "body": "- bodyStartOptional: fold the unreachable final 'return true' into the\n  element check so the line is covered by the existing element cases (the\n  only realistic first-child node types are element/text/comment).\n- CHANGELOG: use _start_ (underscore emphasis) to match prettier's default.",
          "is_bot": false,
          "headline": "fix: restore 100% coverage + markdown emphasis style",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T08:06:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1a174e2fc77f70c649a937e73d3787b1d90c0f3",
          "body": "Add a new `removeOmittedHtmlStartTags` option (MinifierOptions field +\nHtmlMin doRemoveOmittedHtmlStartTags()/isDoRemoveOmittedHtmlStartTags() +\nHtmlMinInterface), OFF BY DEFAULT, that also omits the structural START tags\nwhere the HTML5 spec allows:\n\n- <html>: unless its first child is a comment.\n-\n[…]\ntartOptional truth table (3 tags x first-child kinds x the\nattribute guard), plus flag on/off output tests and a MinifierOptions wiring\ntest. Full suite green (374 tests); phpstan / cs / rector clean.",
          "is_bot": false,
          "headline": "feat(omission): opt-in <html>/<head>/<body> start-tag omission",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T07:59:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "47aa9a6a0c64b1a9c12654cd1ccb51daaa5bde43",
          "body": "Omit thead/tbody/tfoot/caption/colgroup end tags",
          "is_bot": false,
          "headline": "Merge pull request #43 from akankov/feature/missing-end-tag-rules",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T07:33:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c204425c5b488473c2e858b551e9570040639316",
          "body": "…mSuspiciousOrder)\n\nstr_contains(CONST, $var) reads as a swapped-argument call to Phan. strspn\nexpresses 'starts with ASCII whitespace' directly and handles empty text,\nwith identical behaviour.",
          "is_bot": false,
          "headline": "fix(omission): use strspn for leading-whitespace check (Phan PhanPara…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T07:22:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "acaa8341550ed78c6f9c35f344d28c7502ebed1f",
          "body": "Implement the remaining optional END-tag rules from the WHATWG spec:\n\n- thead: end tag omittable when followed by tbody or tfoot.\n- tbody: omittable when followed by tbody/tfoot, or at end of parent.\n- tfoot: omittable at end of parent.\n- caption / colgroup: omittable unless immediately followed by \n[…]\nption/colgroup via explicit DOM so the raw sibling is deterministic) plus\nfull-pipeline output tests asserting the closing tags are dropped.\n\nFull suite green (352 tests); phpstan / cs / rector clean.",
          "is_bot": false,
          "headline": "feat(omission): omit thead/tbody/tfoot/caption/colgroup end tags",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T07:11:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f0f4dc8fef8cbd68fc421ed617ee60cbace178d5",
          "body": "Refactor: rule-dispatch table for conditional end tags",
          "is_bot": false,
          "headline": "Merge pull request #42 from akankov/feature/tag-omission-and-refactor",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-08T06:55:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "619f2e656a904c903998782d5029e8b494697960",
          "body": "…rule\n\nCI's php-cs-fixer (3.95.4) enforces provide<Test>Cases naming; rename\nconditionalEndTagCases -> provideConditionalEndTagOmissionCases.",
          "is_bot": false,
          "headline": "test(omission): rename data provider per php_unit_data_provider_name …",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T21:13:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c6df650aa2330f846d6dc7eb3518e3009b4d7fd4",
          "body": "Replace OptionalTagOmission's ~280-line conditional-end-tag boolean with a\nper-tag match dispatch plus small rule helpers (followedByOrEndOfParent,\npEndTagOptional) and named constants (SOURCE_PARENTS, P_FOLLOWED_BY,\nP_TRAILING_DISALLOWED_PARENTS). Behaviour-preserving.\n\n- The match is defaultless (\n[…]\nnning each conditional tag's truth table,\n  including the deliberate dt-grouped-with-dd deviation, so the refactor is\n  provably equivalent.\n\nFull suite green (333 tests); phpstan / cs / rector clean.",
          "is_bot": false,
          "headline": "refactor(omission): rule-dispatch table for conditional end tags",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T21:05:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "83bcea460fd7f8063da5b5f5250481ff4ad62d5d",
          "body": "Release v2.8.1",
          "is_bot": false,
          "headline": "Merge pull request #41 from akankov/release/v2.8.1",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:38:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea3459d258d92233050bda42b85a3a7fb82f17b8",
          "body": null,
          "is_bot": false,
          "headline": "chore(release): v2.8.1",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:32:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0c05b2507c6d2b297b5075c00703b95c0b4a3ab5",
          "body": "Complete master → main rename (CI trigger, badges, infection)",
          "is_bot": false,
          "headline": "Merge pull request #40 from akankov/chore/master-to-main",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:21:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aec56e6b39d454d404d13a120cae407c257e0ecd",
          "body": "The default branch was renamed master -> main on GitHub. Update the\nin-repo references that still pointed at master:\n\n- ci.yml push trigger `branches: [main]` (so pushes to main run CI) and\n  the Stryker-dashboard comment.\n- README codecov and Stryker mutation-testing badge URLs (branch/main).\n- infection.json5 stryker.badge \"main\" and its comment.\n\nRemaining \"master\" hits are the English word (changelog/test comments)\nand external URLs in a vendored benchmark fixture — intentionally left.",
          "is_bot": false,
          "headline": "chore: complete master -> main rename (CI trigger, badges, infection)",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:16:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1f93778fc98cb99fa7b5c2d732225e4b6f25d382",
          "body": "Anchor .gitignore patterns to the repo root",
          "is_bot": false,
          "headline": "Merge pull request #39 from akankov/chore/gitignore-anchor",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:08:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9f9c7f9fbee1a3fde03bdfd691831e74dd42338d",
          "body": "Style consistency with the twig/laravel siblings, which anchor every\npattern (`/vendor/`, `/build/`, dir entries with a trailing slash).\n\nThe root previously used the bare form (`vendor`, `.phpstan.cache`), which\nalso matched at any depth — quietly covering the benchmarks/ subproject's\nphpstan / cs-\n[…]\nignoring those, so this\nalso completes benchmarks/.gitignore with the three missing cache entries.\nVerified: every path ignored before is still ignored; git status shows\nonly the two .gitignore edits.",
          "is_bot": false,
          "headline": "chore: anchor .gitignore patterns to the repo root",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T17:01:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ade40d6ea208c0004c35da790661f5553f210e92",
          "body": "Per-run placeholder nonce + encapsulate broken-html map",
          "is_bot": false,
          "headline": "Merge pull request #38 from akankov/feature/per-call-nonce",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T16:39:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ba28f02592283e38b4fc4689d0606d91bc7849d1",
          "body": "Follow-up to the per-run nonce change:\n- Import ReflectionProperty and use \\is_string() per the project CS rules\n  (global_namespace_import + native_function_invocation).\n- Add testEntityRestoreMapIsCachedWithinARun. buildEntityRestoreMap() is\n  called once per run via HtmlMin, and its cached-return\n[…]\n because the map persisted across runs process-wide. Now that\n  reset() clears it per run, a test must call putReplacedBack twice within one\n  run to exercise the cache and keep line coverage at 100%.",
          "is_bot": false,
          "headline": "test(parser): satisfy CS style + restore 100% line coverage",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T16:12:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3015fcee0b949b6ce1d0fd0bc5b7c91c4a531d0e",
          "body": "… map\n\nHarden the libxml entity-preserving placeholder layer. No public API change\nand no change to the minified output of any input (308 tests unchanged).\n\n- HtmlParser::reset() now regenerates the random placeholder nonce, and every\n  cache derived from it (urlCharPlaceholders, entityCharPlacehold\n[…]\nstatic, only used internally).\n\nTests: testResetRegeneratesPlaceholderNoncePerRun (reflection-observed, was\nred before the reset() change) and testAdversarialPlaceholderShapedInput-\nSurvivesRoundTrip.",
          "is_bot": false,
          "headline": "refactor(parser): per-run placeholder nonce + encapsulate broken-html…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-06-07T15:58:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7012730aa0a0f5eba2684995bea985849ae4d861",
          "body": "docs(changelog): cut v2.8.0",
          "is_bot": false,
          "headline": "Merge pull request #37 from akankov/release/v2.8.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T17:22:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0757247505edb7db4eae38abebb1951b9ad24eee",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.8.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T17:17:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "156b6d2ed33e1c1c7750d54aaa9b9e0d188f0e0d",
          "body": "…eprocessing\n\nrefactor: drop redundant </html>-trim from HtmlParser::parse()",
          "is_bot": false,
          "headline": "Merge pull request #36 from akankov/refactor/drop-redundant-parser-pr…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T17:13:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1ca2e916622ebea876e1410c4f287eb5c6db7565",
          "body": "libxml already relocates content after a closing </html> back into the\nbody, so the explicit `preg_match`/`str_replace` trim was a no-op for every\nwell-formed and single-</html> input — verified byte-identical with and\nwithout the block on PHP 8.3/8.4/8.5.\n\nThe only observable change is on pathologi\n[…]\n> 77.4%; line coverage stays 100%; full `make ci` green.\n\nNote: the sibling pre-<!DOCTYPE> junk strip was investigated and is NOT\nredundant (libxml keeps element junk before the doctype), so it stays.",
          "is_bot": false,
          "headline": "refactor: drop redundant </html>-trim from HtmlParser::parse()",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T17:07:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "178cbb95c2fd5cd117281107db1816f0faf0f4ec",
          "body": "test: raise MSI to ~77% via attribute-removal scoping tests",
          "is_bot": false,
          "headline": "Merge pull request #35 from akankov/test/kill-observable-mutants",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T16:48:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1386ba9126ca9e2b91c60082631710c08b2e4ffb",
          "body": "Add behavioral tests that pin the *scoping* of the deprecated/default\nattribute removals in OptimizeAttributes — killing ~11 mutants that the\npositive-only cases left alive:\n\n- type=text/css is stripped only on rel=stylesheet links, not rel=preload\n  (isolated from the broader style/link rule to mak\n[…]\nuits,\n(string) casts that never see null, and regex mutations made unreachable by\nupstream stripos() guards — so they are intentionally not chased. Full\n`make ci` green; MSI 76.8%, line coverage 100%.",
          "is_bot": false,
          "headline": "test: raise MSI to ~77% via attribute-removal scoping tests",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T16:35:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "140941535cbf6db2b271e251a870b2fb4e30b377",
          "body": "docs(changelog): cut v2.7.1",
          "is_bot": false,
          "headline": "Merge pull request #34 from akankov/release/v2.7.1",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T15:38:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "00e82652ecb7fd1b979b57898cfc123f5fcb05d8",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.7.1",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T15:32:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4d4f16d0eab7e5f9ecf53e7da4e465de3d31f4aa",
          "body": "test: reach 100% line coverage, ratchet CI floors to 100/74",
          "is_bot": false,
          "headline": "Merge pull request #33 from akankov/test/finish-coverage",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T11:06:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e095f95ee1b94c86394063009144aacd7d80db26",
          "body": "Cover the last ~7% of library lines with behavioral tests and restructure\nthe remaining unreachable defensive type-guards (invert-guard, collapse\nternaries) rather than masking them with coverage-ignore annotations.\n\nTwo small robustness touches fell out of the coverage chase and are kept:\n- HtmlPar\n[…]\nted in both Makefile and ci.yml to lock the milestone in:\nMIN_LINE_COVERAGE 90 -> 100, MIN_MSI / MIN_COVERED_MSI 72 -> 74.\nFull `make ci` green; Infection reports 100% mutation code coverage, 76% MSI.",
          "is_bot": false,
          "headline": "test: reach 100% line coverage, ratchet CI floors to 100/74",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T10:29:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0ab2a0f9d5791c2401691b9270abc929730b8a47",
          "body": "test: cover HtmlParser to 100% (94.7% → 98.2% overall)",
          "is_bot": false,
          "headline": "Merge pull request #32 from akankov/test/cover-htmlparser",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T10:00:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18ab7f7185d5782608583b96675e00b326bd7bb7",
          "body": "Raises overall coverage 94.7% -> 98.2% by covering HtmlParser (54 uncovered\n-> 0):\n\n- Direct HtmlParserTest for the public static utilities that the pipeline\n  doesn't exercise: serialize(), innerHtml() (incl. ownerless element),\n  setInnerHtml() (replace / empty / detached / unparseable), findAll()\n[…]\noot-null guard into a positive\n  condition (behaviour identical; removes an uncoverable statement).\n\nFull make ci green (289 tests x 8.3/8.4/8.5, PHPStan max, Phan, Rector,\ncoverage 98.2%, Infection).",
          "is_bot": false,
          "headline": "test: cover HtmlParser to 100% (public utilities + parser edges)",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T07:25:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "34fb2b74333edeb2f0bdf1d5d1eb0f6bb860acd6",
          "body": "test: raise coverage + MSI — CLI/Middleware/minifier edges + lock defaults",
          "is_bot": false,
          "headline": "Merge pull request #31 from akankov/test/raise-coverage-and-msi",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-31T07:11:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cfeb2b0044b1db1e2d85a004a4c619f5b28dec77",
          "body": "First pass of raising coverage (93.4% -> 94.7%) and mutation score (74.1% ->\n75.4%):\n\n- MinifierOptions: a test pinning all 31 constructor defaults — kills the 23\n  escaped TrueValue/FalseValue mutants and locks the documented \"no-arg =\n  pre-2.2 defaults\" contract.\n- Cli: error-path tests (-o witho\n[…]\n InlineCssMinifier 100% covered.\n\nFull make ci green (271 tests x 8.3/8.4/8.5, PHPStan max, Phan, Rector,\ncoverage 94.7%, Infection). Test-only except the behaviour-preserving Cli\nread simplification.",
          "is_bot": false,
          "headline": "test: cover CLI/Middleware/minifier edges, lock MinifierOptions defaults",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T22:08:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c3d5ac602439e40914d9c4a1ee248fb21c1c6be3",
          "body": "docs(changelog): cut v2.7.0",
          "is_bot": false,
          "headline": "Merge pull request #30 from akankov/release/v2.7.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T21:22:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "471b001b377996599805778b8b17eb6a87670a06",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.7.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T21:17:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f0d0907c2adc31c784b7108c2a2076d1dfbc8ba9",
          "body": "refactor: extract doctype string building into Doctype (decomposition, 5/n)",
          "is_bot": false,
          "headline": "Merge pull request #29 from akankov/refactor/extract-doctype-builder",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T21:08:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3d0f377f0f80ff42078c4b235ce184462b061fbd",
          "body": "Fifth (small) decomposition slice (HtmlMin 1249 -> 1220 lines). getDoctype()'s\nDOM-walk-and-build logic moves to a new Internal\\Doctype::serialize(), sitting\nnext to the existing Internal\\DoctypeKind classifier — the two halves of the\ndoctype concern (build the string / categorise it) now live toget\n[…]\now directly tested (Doctype is 100% covered).\nBehaviour-preserving: full make ci green (255 tests x 8.3/8.4/8.5, PHPStan max,\nPhan, Rector, coverage 93.4%, Infection). Internal — no public API change.",
          "is_bot": false,
          "headline": "refactor: extract doctype string building into Doctype::serialize()",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T18:05:34Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ff1130b1c76d4fb98f972e202898abd07a3f1a6f",
          "body": "…lizer\n\nrefactor: extract whitespace passes into WhitespaceNormalizer (decomposition, 4/n)",
          "is_bot": false,
          "headline": "Merge pull request #28 from akankov/refactor/extract-whitespace-norma…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T17:55:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "72318fa3a2cce2117029aa7de4ee6c959df459ca",
          "body": "The two `instanceof` guards in sumUp() were unreachable: HtmlParser::findAll()\nwith a tag selector only ever returns DOMElements, and with //text() only\nDOMText, so the `continue` branches never executed (Codecov flagged them as the\n2 missing lines). They existed only to narrow findAll()'s generic D\n[…]\n-node SplObjectStorage generic to <DOMNode>:\ncollectDescendantTextNodes() already takes DOMNode and offsetExists() works on\nany node. WhitespaceNormalizer is now 100% line-covered. Full make ci green.",
          "is_bot": false,
          "headline": "refactor: drop dead instanceof guards in WhitespaceNormalizer",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T17:41:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c61a2206906af1853213d4d1b84e2a82c7785c2f",
          "body": "Fourth decomposition slice (HtmlMin 1359 -> 1249 lines). The two whitespace-\ncollapsing DOM passes — removeWhitespaceAroundTags() and sumUpWhitespace()\n(plus collectDescendantTextNodes() and their three static-data members:\n$regExSpace, $trimWhitespaceFromTags, $skipTagsForRemoveWhitespace) — move i\n[…]\nlements, //text() always yields text nodes).\n\nBehaviour-preserving: full make ci green (251 tests x 8.3/8.4/8.5, PHPStan max,\nPhan, Rector, coverage 93.1%, Infection). Internal — no public API change.",
          "is_bot": false,
          "headline": "refactor: extract whitespace passes into WhitespaceNormalizer",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T16:25:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "77f7fc7cabbc1c24452da7bcb887b87846ff1dcf",
          "body": "…inifier\n\nrefactor: extract inline CSS/JS minification (decomposition, 3/n)",
          "is_bot": false,
          "headline": "Merge pull request #27 from akankov/refactor/extract-inline-content-m…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T16:05:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "515ee31505c9869ebd0fd6afb063e3aa119f2a84",
          "body": "Third decomposition slice (HtmlMin 1438 -> 1359 lines). The opt-in inline\n`<style>`/`<script>` minification engine — maybeMinifyInlineContent(),\napplyInlineMinifier(), resolveBundledMinifier(), isScriptSafeForJsMinification(),\nand the two pluggable-override Closures — moves into a new\nInternal\\Inlin\n[…]\nInlineContentMinifier is 100% line-covered).\n\nBehaviour-preserving: full make ci green (247 tests x 8.3/8.4/8.5, PHPStan max,\nPhan, Rector, coverage 93.1%, Infection). Internal — no public API change.",
          "is_bot": false,
          "headline": "refactor: extract inline CSS/JS minification into InlineContentMinifier",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T13:46:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e54237ce4cd9b82781010efc62bc603ca9220387",
          "body": "refactor: extract DomSerializer + open library classes for extension",
          "is_bot": false,
          "headline": "Merge pull request #26 from akankov/refactor/extract-dom-serializer",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T13:31:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "95d1394eca15b366c4fc6d319223234e72780dee",
          "body": "A library's classes should be open for extension — consumers legitimately\nsubclass them. Remove `final` (and the `final readonly class` form) from all\nnine classes: HtmlParser, OptionalTagOmission, DomSerializer, the two inline\nminifiers, OptimizeAttributes, MinifierOptions, Cli, MinifierMiddleware.\n[…]\ne plain one (a non-final public\nmethod's param could be used by an override). Behaviour unchanged: full\n`make ci` green (243 tests x 8.3/8.4/8.5, PHPStan max, Phan, Rector, coverage\n92.6%, Infection).",
          "is_bot": false,
          "headline": "refactor: drop `final` from library classes for extensibility",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T13:25:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "825b4f1cb3abfa8819faecc1df419d0945e70d19",
          "body": "Second slice of the HtmlMin decomposition (1704 -> 1438 lines). The DOM-walk\nserializer — domNodeToString(), the attribute-string builder, and the\npartsEndWithSpace()/rtrimParts() whitespace helpers (~210 lines) — plus the\nserializer-only booleanAttributes table and the unquoted-value forbidden-char\n[…]\nmSerializer is 100% line-covered via the pipeline; adds DomSerializerTest with\ndirect toString()/attributesToString() cases and a subclass test exercising the\nBC shim. Internal — no public API change.",
          "is_bot": false,
          "headline": "refactor: extract DOM serialization into DomSerializer",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T12:19:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "748dc92b703622fb81a31720093e9fc8a3cda2a5",
          "body": "…ssion\n\nrefactor: extract optional-end-tag rules (HtmlMin decomposition, 1/n)",
          "is_bot": false,
          "headline": "Merge pull request #25 from akankov/refactor/extract-optional-tag-omi…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T11:20:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be3bc54d67fb641231a3cf52dc42384da8a9c875",
          "body": "Codecov flagged 2 uncovered patch lines from the extraction: the parent-is-null\nbranch in OptionalTagOmission (never hit through the full pipeline, where\nconditional tags always have a parent) and the retained HtmlMin BC shim.\n\nAdds tests/Internal/OptionalTagOmissionTest covering isOptional() rules,\n[…]\n an anonymous HtmlMin subclass — the protected getNextSiblingOfType-\nDOMElement() shim, which also proves the backward-compat delegation works.\nBoth previously-missing lines are now covered; +6 tests.",
          "is_bot": false,
          "headline": "test: add direct OptionalTagOmission unit tests (cover extracted class)",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T09:54:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8aa5003e47ee41c19cd9d09c347f647ca2512a4b",
          "body": "First slice of decomposing the HtmlMin god class (2023 -> 1704 lines). The\n280-line domNodeClosingTagOptional() — the WHATWG optional-end-tag rule set,\nits two tag-list arrays, and the memoisation cache — moves verbatim into a new\nInternal\\OptionalTagOmission class with an isOptional(DOMNode): bool \n[…]\nubclass.\n\nBehaviour-preserving: 234 tests green (the tag-omission fixtures are the net),\nPHPStan level max, Phan, PHP-CS-Fixer clean, Infection passes at the 72 floor.\nInternal — no public API change.",
          "is_bot": false,
          "headline": "refactor: extract optional-end-tag rules into OptionalTagOmission",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T09:35:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d4ce8479ad643ad1ac3d6272966960e3e98cf456",
          "body": "fix: collision-proof entity placeholders with a per-process nonce",
          "is_bot": false,
          "headline": "Merge pull request #24 from akankov/fix/placeholder-collision-nonce",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T09:15:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "30e099856a0eb87fbffaf39059cd9ce67d291bac",
          "body": "The parser masks libxml-hostile characters (& | + % @ [ ] { }) with placeholder\ntokens like `____HTMLMIN_AMP____` before handing HTML to libxml, then reverses\nthem after serialization. The reverse pass was a blind strtr, so caller content\nthat literally contained a token was corrupted: `<p>____HTMLM\n[…]\nrn: not a real DoS —\nPCRE's backtrack_limit bounds it (returns null, handled by the greedy fallback);\npathological attribute soup and 5000-deep nesting both minify in <25ms. No\nchange warranted there.",
          "is_bot": false,
          "headline": "fix: collision-proof entity placeholders with a per-process nonce",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-30T08:55:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2cfab44f5cf1a3882d879d58d4183f93105fa3dc",
          "body": "…movals\n\nrefactor: data-drive default-attribute removals",
          "is_bot": false,
          "headline": "Merge pull request #23 from akankov/refactor/data-driven-attribute-re…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T21:12:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ebde632b59ec3f1adb0412bca0d1e5d308409efe",
          "body": "Replace 13 repetitive `if ($tag === X && $attrName === Y && $attrValue === Z)`\nblocks with two declarative lookup tables (DEFAULT_ATTRIBUTE_REMOVALS keyed by\ntag→attr, and DEFAULT_ATTRIBUTE_REMOVALS_ANY_TAG for the tag-independent ones)\nplus two `?? null === $attrValue` lookups. ~50 lines of branchi\n[…]\nng it removes\n~90 well-killed mutants and adds ~4 — mechanically lowering killed/total even\nthough the code is simpler. Improving the code is the goal; the score follows\nthe smaller mutant population.",
          "is_bot": false,
          "headline": "refactor: data-drive default-attribute removals in OptimizeAttributes",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T21:05:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fe699bb9b94b22355447728b78ca7dd8650fb992",
          "body": "docs: add Codecov + Stryker (MSI) badges and CI reporting",
          "is_bot": false,
          "headline": "Merge pull request #22 from akankov/docs/coverage-mutation-badges",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T19:29:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1c6c88c5b102bfe55ecbc87aefa546714fe20b62",
          "body": "CI now uploads line coverage to Codecov and publishes the mutation score to\nthe Stryker Mutator dashboard; the README shows both badges alongside the\nexisting CI/Packagist ones.\n\n- ci.yml: codecov/codecov-action@v5 (clover, fail_ci_if_error: false) and a\n  STRYKER_DASHBOARD_API_KEY env on the Infect\n[…]\nis unaffected.\n\nReporting only — no runtime or test behaviour change. Both badges stay blank\nuntil the CODECOV_TOKEN and STRYKER_DASHBOARD_API_KEY repository secrets are\nadded and a master build runs.",
          "is_bot": false,
          "headline": "docs: add Codecov + Stryker (MSI) badges and CI reporting",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T19:23:07Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f44be8672de06565612c772ce7e0d448136df810",
          "body": "test: kill OptimizeAttributes mutants, raise MSI floor to 74",
          "is_bot": false,
          "headline": "Merge pull request #21 from akankov/test/kill-mutants-observer-attrs",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T19:09:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "799078aa6fbe3f9d9d90d40a8a37b500bd63a53d",
          "body": "Second mutant-killing pass, targeting the attribute-removal observer (72\nescaped — the densest behavioural cluster after the scanners). The existing\nprovideDefaultAttributesAreRemovedCases proves each rule *fires*, but the\n`tag && attr && value` conjunctions survived mutation: nothing asserted a rul\n[…]\nther value, other tag).\n\nResult: OptimizeAttributes 72 -> 44 escaped, overall MSI ~73% -> ~75%. Floor\nraised 72 -> 74 (~1.5pp headroom). +51 tests (229 total). Test-only — no\nruntime behaviour change.",
          "is_bot": false,
          "headline": "test: kill OptimizeAttributes mutants, raise MSI floor to 74",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T19:01:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e317c64771fb2d23d7a29d92b9dea508fd6ddb0b",
          "body": "test: kill escaped mutants in inline minifiers, raise MSI floor to 72",
          "is_bot": false,
          "headline": "Merge pull request #20 from akankov/test/kill-escaped-mutants",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T18:26:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c57ac051d5cc3159b1ad0b793ca016864b4b2e14",
          "body": "…o 72\n\nMutation testing reported MSI ~73% with 562 escaped mutants — covered code\nwhose behaviour no assertion actually checks. This hardens the two bundled\nminifiers (the freshly-touched, highest-risk files) with boundary-exact\ncharacterization tests.\n\nThe trick with mutation testing is that bounda\n[…]\n are dominated by index-arithmetic and equivalent mutants\n(e.g. `$len === 0` -> `=== -1` on empty input is identical) that are low-value\nor impossible to kill. Test-only — no runtime behaviour change.",
          "is_bot": false,
          "headline": "test: kill escaped mutants in the inline minifiers, raise MSI floor t…",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T18:16:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6d0762fc32193c590781e1097b2c4bca55248f8e",
          "body": "ci: add code coverage + Infection mutation testing",
          "is_bot": false,
          "headline": "Merge pull request #19 from akankov/ci/coverage-and-mutation-testing",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T17:29:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4e897a78d8a8543b3cc80f86cd74de8777bf6b1b",
          "body": "This week three bugs shipped in code that already had tests — a test-\neffectiveness gap, not a quantity one. Measuring revealed why: 91.89% line\ncoverage but Infection finds 562 \"escaped\" mutants (covered code whose\nbehaviour no assertion actually checks), for a ~73% MSI. Line coverage was\nhiding we\n[…]\ntions harden (the 562 escaped mutants are the backlog).\n\nAlso: ignore the 0-byte tempnam('min…') file Infection drops in CWD per run.\nDev-only — no change to the published package or its runtime deps.",
          "is_bot": false,
          "headline": "ci: add code coverage + Infection mutation testing",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T17:16:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0cff66056d977efbf69da8e0cb757e52da242393",
          "body": "fix: harden bundled inline CSS/JS minifiers against three edge cases",
          "is_bot": false,
          "headline": "Merge pull request #18 from akankov/fix/inline-minifier-edge-cases",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T15:32:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4c5e76acd9e60e7053f62baeb2f042c48d1af797",
          "body": "The v2.6.0 bundled minifiers are context-aware character scanners, but\neach had a context-free shortcut that could corrupt opted-in output:\n\n- CSS: the trailing-`;` optimisation ran as a blanket str_replace(';}','}')\n  over the whole output, reaching into verbatim strings — content:\"x;}y\"\n  became c\n[…]\n braces.\n\nAdds 5 behavior tests (written red-first) plus a full-pipeline integration\ncase. Also ignores the release-scoped baseline.md and repoints a dead doc\nreference. CHANGELOG entry under [2.6.1].",
          "is_bot": false,
          "headline": "fix: harden bundled inline CSS/JS minifiers against three edge cases",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-29T15:28:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7dd466f50e1f5ab0f1ef070102a6768429301cb7",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.6.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-28T07:19:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eb58d3c59d202bab3435f8b6a881b81114acdcd3",
          "body": "Adds an \"akankov/html-min (inline)\" adapter that enables doMinifyInlineCss\n/ doMinifyInlineJs, so the report shows the compression gain of the opt-in\ntoggles alongside the untouched default-config row. Regenerates latest.md\n(now 6 adapters x 15 fixtures): inline minification improves the average\ngzipped ratio 90.7% -> 87.6% for a ~1.9 -> 2.1 ms/op cost, 0 parse failures.",
          "is_bot": false,
          "headline": "chore(bench): add inline-minify variant adapter and regenerate report",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-28T07:16:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0fcd6b860a9a0ee1a67680836c2d348f8cd538bd",
          "body": "feat: opt-in inline CSS and JS minification",
          "is_bot": false,
          "headline": "Merge pull request #17 from akankov/feat/inline-css-js-minification",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-28T07:01:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "235c99d588c11ac235c91f4655796940280d9c82",
          "body": "The new inline-heavy-landing real-world fixture bumps the real-world tier\n5 -> 6 and the total corpus 14 -> 15, which CorpusTest pins. Also reformats\nCHANGELOG.md to Prettier style (nested-list indentation) so md-check passes.",
          "is_bot": false,
          "headline": "test: update benchmark corpus counts and format CHANGELOG",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-28T06:56:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b440cf6bacc65c4f9b5a2e47847b327002f189b2",
          "body": "Inline <style> and <script> contents previously round-tripped untouched.\nTwo new toggles minify them with bundled, zero-dependency, conservative\nminifiers:\n\n- doMinifyInlineCss() strips CSS comments and collapses whitespace while\n  preserving string and url(...) contents.\n- doMinifyInlineJs() does A\n[…]\n buggy bundled minifier\nis logged via PSR-3 and falls back to the original source.\n\nWires the toggles through MinifierOptions, HtmlMinInterface, and the CLI\n(--minify-inline-css / --minify-inline-js).",
          "is_bot": false,
          "headline": "feat: add opt-in inline CSS and JS minification",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-28T06:49:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "07a3a9eccab73f5d4da672951732133c68239446",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): cut v2.5.1",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-13T07:10:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7dc3fa7e2733cb7ed0eae0bc0ce7e5bd37e446c6",
          "body": null,
          "is_bot": false,
          "headline": "Merge pull request #16 from akankov/remove-voku-references",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-13T07:08:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "09d8a50dd4277747779ee775493f80506cb850a7",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove legacy upstream references",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-13T06:59:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "187ca699b092448df97477c43b1f896a3ffbb4f6",
          "body": "Promote the [Unreleased] PHAR-build section that landed via PR #15\nto a dated 2.5.0 release. Completes the CLI distribution story:\nv2.4.0 added the Composer-bin path, v2.5.0 adds the standalone PHAR.",
          "is_bot": false,
          "headline": "docs(changelog): cut v2.5.0",
          "author_name": "Aleksei Kankov",
          "author_login": "akankov",
          "committed_at": "2026-05-07T17:15:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 19,
      "commits_last_year": 203,
      "latest_release_at": "2026-06-19T18:00:08Z",
      "latest_release_tag": "v2.11.0",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 11,
      "days_since_latest_release": 37,
      "mean_days_between_releases": 4.8
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 100,
      "has_issue_template": true,
      "has_code_of_conduct": true,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "akankov/html-min",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "compression",
            "html",
            "minify",
            "minifier",
            "compress"
          ],
          "ecosystem": "packagist",
          "matches_repo": true,
          "registry_url": "https://packagist.org/packages/akankov/html-min",
          "is_deprecated": false,
          "latest_version": "v2.11.0",
          "repository_url": "https://github.com/akankov/html-min",
          "versions_count": 21,
          "total_downloads": 204069,
          "dependents_count": 2,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 69788,
          "first_published_at": null,
          "latest_published_at": "2026-06-19T17:44:04Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 37
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 2,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 34986,
      "source_files_sampled": 75,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "benchmarks/composer.json",
        "composer.json"
      ],
      "advisories": {
        "error": "No resolved dependencies carried a version and a supported ecosystem",
        "scope": "repository_graph",
        "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": 22,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "packagist"
      ],
      "dependencies": [
        {
          "name": "psr/http-factory",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^1.0"
        },
        {
          "name": "psr/http-message",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^1.1 || ^2.0"
        },
        {
          "name": "psr/http-server-middleware",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^1.0"
        },
        {
          "name": "psr/log",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^3.0"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "psr/http-factory",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "psr/http-message",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "psr/http-server-middleware",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "psr/log",
            "direct": true,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "abordage/html-min",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "akankov/html-min",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "ext-dom",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "ext-libxml",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "ext-mbstring",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "ext-simplexml",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "ext-zlib",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "friendsofphp/php-cs-fixer",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "infection/infection",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "nyholm/psr7",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "phan/phan",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "php",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "phpbench/phpbench",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "phpstan/phpstan",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "phpunit/phpunit",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "rector/rector",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "voku/html-min",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          },
          {
            "name": "zaininnari/html-minifier",
            "direct": false,
            "version": null,
            "ecosystem": "packagist"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 22,
        "direct_count": 4,
        "indirect_count": 18
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 60,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 1,
      "top_contributors": [
        {
          "type": "User",
          "login": "akankov",
          "commits": 196,
          "avatar_url": "https://avatars.githubusercontent.com/u/1974569?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [
        ".php-cs-fixer.dist.php",
        "phpstan.neon"
      ],
      "has_editorconfig": true,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 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": 10,
            "reason": "17 out of 17 merged PRs checked by a CI test -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/11 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": null,
            "reason": "no workflows found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 10,
            "reason": "update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 0 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": null,
            "reason": "no dependencies found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 10,
            "reason": "SAST tool is run on all commits",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file 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": null,
            "reason": "No tokens found",
            "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": "a766e4039defbda13532d83bcf8cc7efc6a0c23f",
        "ran_at": "2026-07-27T15:59:22Z",
        "aggregate_score": 6.4,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": true,
      "has_dependabot_config": true
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-27T15:55:50Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-07-27T15:55:35Z",
      "ci_last_conclusion": null,
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/akankov/html-min",
    "host": "github.com",
    "name": "html-min",
    "owner": "akankov"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 66,
      "inputs": {
        "security": 64,
        "vitality": 83,
        "community": 52,
        "governance": 50,
        "engineering": 80
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 83,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "commits_last_year": 203,
              "human_commit_share": 0.94,
              "days_since_last_push": 0,
              "active_weeks_last_year": 11
            },
            "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": "11/52 weeks with commits",
                "points": 7.6,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "203 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 203
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 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": 19,
              "latest_release_tag": "v2.11.0",
              "releases_from_tags": false,
              "days_since_latest_release": 37,
              "mean_days_between_releases": 4.8
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "19 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 19
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 37 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 37
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~4.8 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 4.8
                    }
                  }
                ],
                "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": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "moderate",
        "name": "Community & Adoption",
        "value": 52,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 2,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "2 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "excellent",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "has_issue_template": true,
              "has_code_of_conduct": true,
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 7.2,
                "status": "met",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": null,
            "notes": [],
            "value": 68,
            "inputs": {
              "packages": [
                "akankov/html-min"
              ],
              "dependents": 2,
              "ecosystems": "packagist",
              "total_downloads": 204069,
              "monthly_downloads": 69788
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "69,788 downloads/month across packagist",
                "points": 64.6,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 69788,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "2 packages depend on it",
                "points": 3.2,
                "status": "partial",
                "details": [
                  {
                    "code": "registry_dependents",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 50,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 72,
            "inputs": {
              "merged_prs": 60,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "60/60 decided PRs merged",
                "points": 38.2,
                "status": "met",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 60,
                      "decided": 60
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/11 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 38,
            "inputs": {
              "followers": 0,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "akankov",
              "public_repos": 15,
              "account_age_days": 5126
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "personal (user) account",
                "points": 10,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_personal",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": "not applicable to user accounts",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_applicable_to_user_accounts",
                    "params": {}
                  }
                ],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "0 followers of akankov",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 0,
                      "login": "akankov"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "15 public repos, account ~14 yr old",
                "points": 20.8,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 15
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 14
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "akankov/html-min"
              ],
              "ecosystems": "packagist",
              "any_deprecated": false,
              "min_days_since_publish": 37
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on packagist",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 37 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 37
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "21 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 21
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 80,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": true,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "1 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "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": ".php-cs-fixer.dist.php, phpstan.neon",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".php-cs-fixer.dist.php, phpstan.neon"
                    }
                  }
                ],
                "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": "17 out of 17 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 65,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": "https://github.com/akankov/html-min",
              "has_readme": true,
              "has_docs_dir": false,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://github.com/akankov/html-min",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 64,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Dangerous-Workflow, Packaging, Pinned-Dependencies, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "dangerous_workflow",
                    "packaging",
                    "pinned_dependencies",
                    "signed_releases",
                    "token_permissions"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 64,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 13,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 5,
              "scorecard_aggregate": 6.4
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "17 out of 17 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/11 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no workflows found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "update tool detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 0 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": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is run on all commits",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "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": "No tokens found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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": 2
            },
            "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": 55,
        "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": 1,
              "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": "94 of 94 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 94,
                      "sampled": 94
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Pinned-Dependencies. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_pinned_dependencies"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 66,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0.06
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".php-cs-fixer.dist.php, phpstan.neon",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".php-cs-fixer.dist.php, phpstan.neon"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "6 of the last 100 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 6,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "HTML",
              "largest_source_bytes": 34986,
              "source_files_sampled": 75,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "HTML without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "HTML"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/75 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 75,
                      "oversized": 0
                    }
                  }
                ],
                "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",
    "No resolved dependencies carried a version and a supported ecosystem"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-27T15:59:38.222828Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/a/akankov/html-min.svg",
  "full_name": "akankov/html-min",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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