Registro público
Informe de salud del softwareesquema 0.23.0 · métricas 1.13.0 · 2026-07-21 20:37 UTC

mindeng / nom-exif

Exif/metadata parsing library written in pure Rust, both image (JPEG, PNG, HEIC/HEIF, AVIF, TIFF, Phase One IIQ, Fujifilm RAF, Canon CR3) and video/audio (MP4, MOV, 3GP, MKV, WEBM, MKA) files are supported.

RustMIT★ 117 estrellas⑂ 16 forksdesde feb 2024Ver en GitHub ↗

mindeng/nom-exif tiene un índice de salud de 64 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Engineering Quality (81/100) y la más baja, Security (43/100). Se actualizó por última vez hace 1 día. Una sola persona concentra la mayor parte del trabajo reciente.

64
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.

64
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

Min DengCuenta personal
39 seguidores70 repositorios públicosdesde jul 2011

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
crates.ionom-exif3.6.112.56955hace 1 díaexifmetadataparsingparser-implementations

Métricas por categoría

Vitalidad

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

79Bueno · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 1 días
7.6/36Cadencia de commits — 11/52 semanas con commits
18/18Volumen de commits — 274 commits en el último año
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Datos de entrada utilizados
commits_last_year274
human_commit_share1
days_since_last_push1
active_weeks_last_year11
Cómo se puntúa
27/27Publica versiones — 48 versiones publicadas
36/36Recencia de las versiones — última versión hace 40 días
27/27Cadencia de publicación — una versión cada ~3,4 días
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Datos de entrada utilizados
releases_count48
latest_release_tagnom-exif-v3.6.1
releases_from_tagsno
days_since_latest_release40
mean_days_between_releases3,4

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
33.5/60Estrellas — 117 estrellas
9.8/25Forks — 16 forks
0/15Observadores — 2 observadores
Datos de entrada utilizados
forks16
stars117
watchers2
growth_stateorganic
growth_factor_pct100
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (MIT)
0/18Guía CONTRIBUTING
0/13.5Código de conducta
0/7.2Plantilla de issues
0/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno
Cómo se puntúa
54.7/80Descargas mensuales — 12.569 descargas/mes en crates
0/20Dependientes en el registro — no lo informa este ecosistema
Datos de entrada utilizados
packagesnom-exif
dependents
ecosystemscrates
total_downloads155.187
monthly_downloads12.569
Excluidos de la puntuación (sin datos o no aplicable): Dependientes en el registro. Los pesos restantes se han renormalizado.

Sostenibilidad y Gobernanza

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

61Moderado · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
1.3/22.5Distribución de commits — el principal contribuyente firma el 94% de los commits
13.5/13.5Amplitud de contribuyentes — 11 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled11
top_contributor_share0,942
Cómo se puntúa
43.2/46.8Resolución de issues — 92% de issues cerradas
34.4/38.3Aceptación de PR — 18/20 PR decididos fusionados
1.5/15OpenSSF Scorecard: Code-Review — Found 5/30 approved changesets -- score normalized to 1
Datos de entrada utilizados
merged_prs18
open_issues3
closed_issues36
issue_closed_ratio0,923
closed_unmerged_prs2
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
11.5/25Alcance del propietario — 39 seguidores de mindeng
25/25Trayectoria — 70 repos públicos, cuenta de ~15 años
Datos de entrada utilizados
followers39
owner_typeUser
is_verified
owner_loginmindeng
public_repos70
account_age_days5482
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 crates
35/35Recencia de publicación — última publicación hace 1 días
20/20Historial de versiones — 55 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesnom-exif
ecosystemscrates
any_deprecatedno
min_days_since_publish1

Calidad de Ingeniería

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

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

Documentación

100Excelente
Cómo se puntúa
30/30README
25/25Directorio de documentación
15/15Sitio de documentación / página del proyecto — https://crates.io/crates/nom-exif
10/10Descripción del repositorio
10/10Topics — 16 topics
10/10Wiki
Datos de entrada utilizados
topicsexif, heif, jpeg, metadata, mp4, nom, parser, quicktime, rust, matroska, mkv, webm, mov, raw, tiff, cr3
has_wiki
homepagehttps://crates.io/crates/nom-exif
has_readme
has_docs_dir
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?

43En riesgo · 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 — 5 out of 5 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.8/7.5Code-Review — Found 5/30 approved changesets -- score normalized to 1
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
5/5Fuzzing — project is fuzzed
2.5/2.5Licencia — license file detected
7.5/7.5Maintained — 30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — sin datos
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated17
scorecard_versionv5.5.0
checks_inconclusive1
scorecard_aggregate4,3
Excluidos de la puntuación (sin datos o no aplicable): packaging. 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.

53Moderado · 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 — 98 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,98
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
12.6/18Arranque con un solo comando — Cargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml (convención del toolchain, sin ejecutor de tareas)
22/22Pruebas automatizadas
0/11Configuración de lint / formato
11/11Verificación estática de tipos — Rust (tipado estático)
0/10Entorno reproducible
0/10Práctica demostrada con agentes — ningún commit con autoría de agente entre los últimos 100
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_language
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configs
agent_commit_share0
toolchain_manifestsCargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml
dependency_bot_commit_share0
Cómo se puntúa
45/45Código verificable por tipos — Rust (tipado estático)
53.8/55Tamaños de archivo manejables — 1/45 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languageRust
largest_source_bytes87.944
source_files_sampled45
oversized_source_files1

Datos clave

117estrellas de GitHub
11contribuidores
274commits en los últimos 12 meses
1días desde el último push
48versiones publicadas
1factor bus
3issues abiertas
crates.ioecosistemas de paquetes

Advertencias de recopilación de datos

  • No resolved dependencies carried a version and a supported ecosystem

Más detalle

Historial de estrellas y forks 117 ★ / 16 ⇿
117Estrellas
16Forks
44Versiones

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

02040608010012011716142024-072025-072026-07
Mayor 2Menor 14Parche 21

Cada punto abarca 2 días.

OpenSSF Scorecard 4.3 / 10
4.3agregado

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-21 20:37 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests5 out of 5 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
1Code-ReviewFound 5/30 approved changesets -- score normalized to 1
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
10Fuzzingproject is fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
n/dPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Dependencias directas 9
RegistroPaqueteRestricción de versiónManifiesto
crates.ionom8.0Cargo.toml
crates.iothiserror2.0.11Cargo.toml
crates.ioserde1.0Cargo.toml
crates.ioregex1.10Cargo.toml
crates.iochrono0.4Cargo.toml
crates.iotracing0.1.40Cargo.toml
crates.iotokio1.40.0Cargo.toml
crates.iobytes1.7.1Cargo.toml
crates.ioiso6709parse0.1.0Cargo.toml
Todas las dependencias 16

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

RegistroPaqueteVersiónRelación
crates.iobytesdirecta
crates.iochronodirecta
crates.ioiso6709parsedirecta
crates.ionomdirecta
crates.ioregexdirecta
crates.ioserdedirecta
crates.iothiserrordirecta
crates.iotokiodirecta
crates.iotracingdirecta
crates.ioaflindirecta
crates.ioclapindirecta
crates.ionom-exifindirecta
crates.iorandindirecta
crates.ioserde_jsonindirecta
crates.iotest-caseindirecta
crates.iotracing-subscriberindirecta
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": [
        "exif",
        "heif",
        "jpeg",
        "metadata",
        "mp4",
        "nom",
        "parser",
        "quicktime",
        "rust",
        "matroska",
        "mkv",
        "webm",
        "mov",
        "raw",
        "tiff",
        "cr3"
      ],
      "is_fork": false,
      "size_kb": 29166,
      "has_wiki": true,
      "homepage": "https://crates.io/crates/nom-exif",
      "languages": {
        "Rust": 598842,
        "Shell": 696
      },
      "pushed_at": "2026-07-20T03:26:22Z",
      "created_at": "2024-02-15T04:05:16Z",
      "owner_type": "User",
      "updated_at": "2026-07-07T07:20:46Z",
      "description": "Exif/metadata parsing library written in pure Rust, both image (JPEG, PNG, HEIC/HEIF, AVIF, TIFF, Phase One IIQ, Fujifilm RAF, Canon CR3) and video/audio (MP4, MOV, 3GP, MKV, WEBM, MKA) files are supported.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Rust",
      "significant_languages": [
        "Rust"
      ]
    },
    "owner": {
      "blog": "https://mincodes.com",
      "name": "Min Deng",
      "type": "User",
      "login": "mindeng",
      "company": null,
      "location": "Shenzhen",
      "followers": 39,
      "avatar_url": "https://avatars.githubusercontent.com/u/921932?v=4",
      "created_at": "2011-07-18T03:16:28Z",
      "is_verified": null,
      "public_repos": 70,
      "account_age_days": 5482
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "nom-exif-v3.6.1",
          "kind": "other",
          "published_at": "2026-06-11T14:36:21Z"
        },
        {
          "tag": "rexiftool-v0.2.4",
          "kind": "other",
          "published_at": "2026-06-11T14:35:56Z"
        },
        {
          "tag": "v3.6.0",
          "kind": "minor",
          "published_at": "2026-05-28T14:25:18Z"
        },
        {
          "tag": "v3.5.1",
          "kind": "patch",
          "published_at": "2026-05-28T14:10:23Z"
        },
        {
          "tag": "rexiftool-v0.2.3",
          "kind": "other",
          "published_at": "2026-05-23T10:35:47Z"
        },
        {
          "tag": "v3.5.0",
          "kind": "minor",
          "published_at": "2026-05-23T10:40:51Z"
        },
        {
          "tag": "rexiftool-v0.2.2",
          "kind": "other",
          "published_at": "2026-05-20T07:58:15Z"
        },
        {
          "tag": "v3.4.2",
          "kind": "patch",
          "published_at": "2026-05-20T08:08:22Z"
        },
        {
          "tag": "rexiftool-v0.2.1",
          "kind": "other",
          "published_at": "2026-05-12T08:40:16Z"
        },
        {
          "tag": "v3.4.1",
          "kind": "patch",
          "published_at": "2026-05-12T08:29:39Z"
        },
        {
          "tag": "rexiftool-v0.2.0",
          "kind": "other",
          "published_at": "2026-05-10T15:30:53Z"
        },
        {
          "tag": "v3.4.0",
          "kind": "minor",
          "published_at": "2026-05-10T15:56:34Z"
        },
        {
          "tag": "rexiftool-v0.1.0",
          "kind": "other",
          "published_at": "2026-05-10T14:19:37Z"
        },
        {
          "tag": "v3.3.0",
          "kind": "minor",
          "published_at": "2026-05-10T13:14:02Z"
        },
        {
          "tag": "v3.2.0",
          "kind": "minor",
          "published_at": "2026-05-09T15:18:53Z"
        },
        {
          "tag": "v3.1.1",
          "kind": "patch",
          "published_at": "2026-05-09T14:54:46Z"
        },
        {
          "tag": "v3.1.0",
          "kind": "minor",
          "published_at": "2026-05-09T14:54:35Z"
        },
        {
          "tag": "v3.0.0",
          "kind": "major",
          "published_at": "2026-05-09T10:57:25Z"
        },
        {
          "tag": "v2.8.0",
          "kind": "minor",
          "published_at": "2026-05-06T10:07:44Z"
        },
        {
          "tag": "v2.7.0",
          "kind": "minor",
          "published_at": "2026-02-02T06:13:26Z"
        },
        {
          "tag": "v2.6.1",
          "kind": "patch",
          "published_at": "2026-01-31T01:43:50Z"
        },
        {
          "tag": "v2.6.0",
          "kind": "minor",
          "published_at": "2026-01-14T12:15:18Z"
        },
        {
          "tag": "v2.5.4",
          "kind": "patch",
          "published_at": "2025-06-08T01:34:48Z"
        },
        {
          "tag": "v2.5.3",
          "kind": "patch",
          "published_at": "2025-06-07T15:25:06Z"
        },
        {
          "tag": "v2.5.2",
          "kind": "patch",
          "published_at": "2025-06-07T13:36:37Z"
        },
        {
          "tag": "v2.5.1",
          "kind": "patch",
          "published_at": "2025-03-13T10:58:49Z"
        },
        {
          "tag": "v2.4.2",
          "kind": "patch",
          "published_at": "2025-02-24T14:44:16Z"
        },
        {
          "tag": "v2.3.1",
          "kind": "patch",
          "published_at": "2025-02-09T00:30:23Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2025-01-15T04:58:13Z"
        },
        {
          "tag": "v2.2.1",
          "kind": "patch",
          "published_at": "2024-10-23T15:14:23Z"
        },
        {
          "tag": "v2.1.1",
          "kind": "patch",
          "published_at": "2024-10-23T12:08:06Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2024-10-07T03:10:19Z"
        },
        {
          "tag": "v2.0.2",
          "kind": "patch",
          "published_at": "2024-09-18T03:25:23Z"
        },
        {
          "tag": "v2.0.0",
          "kind": "major",
          "published_at": "2024-09-17T15:43:13Z"
        },
        {
          "tag": "v1.5.2",
          "kind": "patch",
          "published_at": "2024-09-05T01:51:20Z"
        },
        {
          "tag": "v1.5.1",
          "kind": "patch",
          "published_at": "2024-08-31T08:52:25Z"
        },
        {
          "tag": "v1.5.0",
          "kind": "minor",
          "published_at": "2024-08-31T04:40:22Z"
        },
        {
          "tag": "v1.4.1",
          "kind": "patch",
          "published_at": "2024-08-25T04:35:46Z"
        },
        {
          "tag": "v1.4.0",
          "kind": "minor",
          "published_at": "2024-08-25T04:27:41Z"
        },
        {
          "tag": "v1.3.0",
          "kind": "minor",
          "published_at": "2024-07-25T10:01:52Z"
        },
        {
          "tag": "v1.2.6",
          "kind": "patch",
          "published_at": "2024-07-13T11:11:02Z"
        },
        {
          "tag": "v1.2.5",
          "kind": "patch",
          "published_at": "2024-07-13T02:34:44Z"
        },
        {
          "tag": "v1.2.4",
          "kind": "patch",
          "published_at": "2024-07-12T15:18:29Z"
        },
        {
          "tag": "v1.2.3",
          "kind": "patch",
          "published_at": "2024-07-12T07:56:47Z"
        },
        {
          "tag": "v1.0.1",
          "kind": "patch",
          "published_at": "2024-02-23T14:30:20Z"
        },
        {
          "tag": "v1.0.0",
          "kind": "major",
          "published_at": "2024-02-20T08:45:48Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2024-02-18T09:35:39Z"
        },
        {
          "tag": "v0.1.1",
          "kind": "patch",
          "published_at": "2024-02-15T06:24:12Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "d0b1412d6f115b256fdbc47ba71475f509186745",
          "body": null,
          "is_bot": false,
          "headline": "release: nom-exif v3.6.1 + rexiftool v0.2.4",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-06-11T14:38:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "13fbaa4e98a9f33c0158e53a1573571df1ffd5e3",
          "body": "* feat: short-circuit MIME detection for legacy QuickTime pnot files\n\nFiles from early consumer cameras (Nikon COOLPIX S5/P5000, Casio EX-Z50,\nFujifilm FinePix older AVI/MOV mixes, etc., ca. 2003-2008) start with a\n`pnot` (preview) atom instead of `ftyp`. Such files were rejected by\n`parse_bmff_mime\n[…]\n track parsing\nworks unchanged.\n\nVerified against a real-world mdat-first MOV (moov at EOF, 2.8 MB\nmdat): CreateDate matches ExifTool output.\n\n---------\n\nCo-authored-by: enjoypi <sre@happyfactory.com>",
          "is_bot": false,
          "headline": "feat: short-circuit MIME detection for legacy QuickTime pnot files (#59)",
          "author_name": "Fan",
          "author_login": "only1zf",
          "committed_at": "2026-06-11T14:31:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a65033ae1829418772284da44ac68915bb6c6294",
          "body": "…+ slice coercion fix",
          "is_bot": false,
          "headline": "release: nom-exif v3.6.0 (2026-05-28) — tokio/tokio-fs feature split …",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-28T14:22:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b839e60e3ccb81bffcc5827b8c48ab499707eaa2",
          "body": null,
          "is_bot": false,
          "headline": "docs: clarify async feature split in module-level doc",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-28T14:20:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6ab4e6577ddb4937a78842c06800106a5807e4b5",
          "body": "so that wasm32-unknown-unknown and similar targets\ncan use the tokio feature for AsyncMediaSource",
          "is_bot": false,
          "headline": "refactor!: split up tokio feature into tokio and tokio-fs",
          "author_name": "Enduriel",
          "author_login": "Enduriel",
          "committed_at": "2026-05-28T14:17:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6150bcb317308acfa709d81b0ec08ac8ebb1492d",
          "body": "…ompat",
          "is_bot": false,
          "headline": "release: nom-exif v3.5.1 (2026-05-28) — slice coercion fix for rkyv c…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-28T14:05:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "872ade504d3940b94bc2c8cbcf72b57d60f8684c",
          "body": "Coverage increased from 72.59% to 95.15%, well above the 86% CI threshold.\nTests cover: container_motion_photo_offset (XMP Container Directory),\nscan_motion_photo error/incomplete branches, parse_motion_photo_offset\nedge cases (MicroVideo gate, invalid offsets), parse_segment size<2,\nextract_exif_data payload<=6, memchr_subslice empty/long needle,\ncheck_jpeg malformed input, and extract_attr_value edge cases.",
          "is_bot": false,
          "headline": "test: add 36 unit tests for jpeg.rs motion photo and edge case paths",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-28T13:59:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dad49e02b13322c75399481cff0b5f106eb33dd6",
          "body": "Comparing `extract_attr_value()` (which returns `Option<&[u8]>`) against byte-string literals relied on automatic unsizing coercion from `&[u8; N]` to `&[u8]`. This fails when a crate like rkyv is in the dependency graph due to its blanket `PartialEq<ArchivedOption<T>>` impl adding a trait resolution candidate.\\n\\nCoerce the literals to slices explicitly via `&b\"MotionPhoto\"[..]`.",
          "is_bot": false,
          "headline": "fix: explicit slice coercion in MotionPhoto attribute comparison",
          "author_name": "Enduriel",
          "author_login": "Enduriel",
          "committed_at": "2026-05-28T13:31:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9bd5ac7ecc3509f0f2a26214ad34536c1a57e98f",
          "body": "…er + LensSerialNumber)",
          "is_bot": false,
          "headline": "release(rexiftool): v0.2.3 — pick up nom-exif 3.5.0 (CameraSerialNumb…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-23T10:34:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "acb56d18f4f73f00d7211fa0de320f3d2955cd28",
          "body": "…alNumber EXIF tags\n\nMinor release adding two standard EXIF tag definitions:\n\n- CameraSerialNumber (0xa431) — camera body serial number (#56)\n- LensSerialNumber  (0xa435) — lens serial number (#57)\n\nBoth additive only, fully backward compatible.",
          "is_bot": false,
          "headline": "release: nom-exif v3.5.0 (2026-05-23) — CameraSerialNumber + LensSeri…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-23T10:30:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f0030f00024d48abf770c916e1975c82a015f648",
          "body": null,
          "is_bot": false,
          "headline": "Add LensSerialNumber",
          "author_name": "Mahmoud Al-Qudsi",
          "author_login": "mqudsi",
          "committed_at": "2026-05-23T09:27:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bc139f1515286a19778f9f184677ddc442690aa7",
          "body": null,
          "is_bot": false,
          "headline": "Add CameraSerialNumber",
          "author_name": "Mahmoud Al-Qudsi",
          "author_login": "mqudsi",
          "committed_at": "2026-05-23T09:25:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "383d0f60cb3f9437df6fcdcaeecde4ef9f369bd7",
          "body": null,
          "is_bot": false,
          "headline": "release(rexiftool): v0.2.2 — pick up nom-exif 3.4.2 PNG streaming fix",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-20T07:57:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "22f41ab80418dfa3478b9efddbfa8e5b8a10a492",
          "body": "…Kind plumbing\n\nPatch release covering two fixes:\n\n- #55: streaming-mode PNG parsing failed on every real PNG with the\n  confusing \"malformed iso-bmff box: PNG: bad signature\" error. The\n  chunk walker now requests the correct skip distance and threads\n  PngPastSignature state so the resumed call do\n[…]\ne at the type level; behaviour change is \"wrong error\nlabel → correct error label\" — strict callers matching specific\nMalformedKind values may see different labels for non-PNG/non-TIFF\nparse failures.",
          "is_bot": false,
          "headline": "release: nom-exif v3.4.2 (2026-05-20) — PNG streaming fix + Malformed…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-20T07:56:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "691c2ed81478865536aaaa1da8718f61580bcd03",
          "body": "…t each call site\n\nCode-review follow-up to 3100a4b. That commit reshaped\n`ParsedError::Failed` to carry `MalformedKind` and removed the\nhardcoded `IsoBmffBox` fallback in `From<ParsedError> for Error`, but\nleft the parallel `From<nom::Err<...>> for crate::Error` blanket impl\nin place — and that one\n[…]\n .. })` assertions in\n  src/ebml/webm.rs now also pin `kind: MalformedKind::EbmlElement`.\n\nCosmetic:\n- Unify the \"skip too many bytes\" comment between parser.rs and\n  parser_async.rs (was asymmetric).",
          "is_bot": false,
          "headline": "refactor(error): drop blanket nom::Err → Error fallback, plumb kind a…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-20T07:34:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3100a4b9a1b04ec2a37b6f383ccafad20e3edad9",
          "body": "…x fallback\n\n`From<ParsedError> for Error` used to hardcode `MalformedKind::IsoBmffBox`\nas the fallback label for every parse failure, regardless of which\nstructural unit actually failed. The pre-existing TODO comment called\nfor \"plumbing the actual MalformedKind through ParsedError so this\nfallback\n[…]\nnd_to_top_level_error`\n  locks the conversion contract.\n\nFollow-up to #55. End-to-end PNG errors now surface as\n`Error::Malformed { kind: PngChunk, ... }` instead of the misleading\n`IsoBmffBox` label.",
          "is_bot": false,
          "headline": "fix(error): plumb MalformedKind through ParsedError, retire IsoBmffBo…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-20T03:23:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a935b8727bc57105a4347dc3b8939f8ce8e09eed",
          "body": "Two coupled defects in `extract_chunks` made every real-world PNG (any\nfile with a non-stripped IDAT) fail in streaming mode with the\nconfusing `malformed iso-bmff box: PNG: bad signature` error:\n\n1. `ClearAndSkip(total - remaining)` requested the wrong skip distance.\n   The parser interprets `Clear\n[…]\nth_state`)\nand a tightened skip-distance assertion in `extract_chunks_skips_large_idat`.\n\nThe pre-existing `IsoBmffBox` mislabel in `error.rs:146` (TODO marked)\nis orthogonal and left for a follow-up.",
          "is_bot": false,
          "headline": "fix(png): streaming chunk walker strands mid-IDAT on retry (#55)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-20T03:03:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "12de89950b6d3798419981a9a61f284178712da5",
          "body": "- actions/checkout: bump build + fuzz jobs from v3 to v4 (coverage job\n  already on v4; v3 inconsistency would surface as deprecation warnings)\n- workflow: document fail_ci_if_error: false — Codecov is badge-only,\n  gate is the separate --fail-under-lines step\n- webm test: correct misleading comment in webm_rejects_non_webm_input\n  (JPEG is rejected at doc_type parse, not at the NotWebmFile branch);\n  tighten assertion to matches!(err, ParsingError::Failed(_))",
          "is_bot": false,
          "headline": "ci/test: address aggregate review nits",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T13:44:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "41fccb3e707d7cef3d9e1e24650f144023d898bc",
          "body": "…h_else\n\n- jpeg.rs:325 `loop { let Some(...) = ... else break; }` → `while let Some(...) = ...`\n  (pre-existing, flagged by rust 1.95 clippy)\n- cr3_moov.rs: collapse `match` with vacuous Err arm → `if let Ok(...)`\n- values.rs: alias `fn(&EntryValue) -> bool` to `Check` in two test fixtures\n  to satisfy `type_complexity` under `--tests --all-features`",
          "is_bot": false,
          "headline": "chore(clippy): silence while_let_loop / type_complexity / single_matc…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T13:40:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b5d27a77f308b9c9f81431ced2dee6ccb515685c",
          "body": "Baseline after spot-fix tests: 88.54% line coverage. Threshold set at\nfloor(achieved) - 2 to leave a small headroom for non-critical PRs while\ncatching genuine regressions.\n\ncargo-llvm-cov 0.8.6 silently no-ops --fail-under-lines when combined\nwith --lcov, so the threshold check runs as a separate\n`cargo llvm-cov report --text` step.",
          "is_bot": false,
          "headline": "ci(coverage): enforce --fail-under-lines 86 threshold",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T11:02:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c65ee1a5e196563078fad78a351b48291c6db0f5",
          "body": "…eta.rs",
          "is_bot": false,
          "headline": "refactor(bbox): delete dead idat.rs and its commented references in m…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:59:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "244b52bff3032af1e5a44093d45d57c4cea33b80",
          "body": null,
          "is_bot": false,
          "headline": "test(webm): tighten NotWebmFile assert and cover Invalid* From arms",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:57:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e691237d1872da9aab0a02c009be03ff21d09f89",
          "body": "…appy path",
          "is_bot": false,
          "headline": "test(webm): cover NotWebmFile, truncation/Need paths, multi-fixture h…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:52:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d125722c54208a06f4e67b1feb8cf67c2474eb39",
          "body": null,
          "is_bot": false,
          "headline": "test(cr3): assert CMT1 explicitly in second-pass test",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:48:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2e1e9f679345e1983b0239dfc9c3d3d639a57588",
          "body": "…-pass",
          "is_bot": false,
          "headline": "test(cr3): cover truncation paths, CMT range extraction, state second…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:45:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4024f54f1e8f3a689521a3b07c645589518bd58f",
          "body": null,
          "is_bot": false,
          "headline": "test(values): tighten variant_default checks for IRational/F32/F64",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:44:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61227ec62b0c808005d82337fed0fa4dfefe756c",
          "body": null,
          "is_bot": false,
          "headline": "test(values): remove spec-out-of-scope datetime/accessor/rational tests",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:40:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b420da630e751d34a19507f1446724ab5546cc6",
          "body": "… arms",
          "is_bot": false,
          "headline": "test(values): cover InvalidShape arms, variant_default, accessor None…",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:35:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14b55512087fbde02e294bb7bf9613cdca30ff2d",
          "body": null,
          "is_bot": false,
          "headline": "test(heif): tighten should_panic with expected message",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:30:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2810a44b0dbdc948a2929670e134e9a0825df8c0",
          "body": null,
          "is_bot": false,
          "headline": "test(heif): cover ClearAndSkip, bad-ftyp, no-meta, and second-pass paths",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:25:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "444bd63ccaeae2a8cddc0707d0e70d850676cd2d",
          "body": null,
          "is_bot": false,
          "headline": "test(travel): address review — rename misleading tests, drop le flag",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:19:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d670ba58b5d0346e1f3daf2390b8016fa2bddd95",
          "body": null,
          "is_bot": false,
          "headline": "test(travel): cover tag==0, format-error, sub-IFD, depth/offset guards",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:13:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5f02ce9971db1aa19e477433de18a9a2705908df",
          "body": "Seven tasks: one per low-coverage file (travel/heif/values/cr3/webm),\nplus idat.rs deletion and a --fail-under-lines CI gate.",
          "is_bot": false,
          "headline": "docs(plan): test coverage spot-fix implementation plan",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T10:05:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "85d38a0d3e459487a66fab33d769635db951c6ce",
          "body": "…+ CI gate)\n\nBaseline 84.72%; target ~87% by adding tests to 5 low-coverage modules,\nremoving dead idat.rs, and adding a --fail-under-lines gate to CI.",
          "is_bot": false,
          "headline": "docs(spec): test coverage spot-fix plan (travel/heif/cr3/values/webm …",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T09:58:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5f1640933896df98fea28dd63cf7db647576b147",
          "body": "Runs `cargo llvm-cov --package nom-exif --all-features` in CI and uploads\nlcov.info to Codecov. Tokenless upload for now (public repo); add\nCODECOV_TOKEN secret later if rate-limiting becomes an issue.",
          "is_bot": false,
          "headline": "ci(coverage): add cargo-llvm-cov job and codecov badge",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-12T09:36:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "94e84dde0a7860d7d6ba2f1999411670e9a62efe",
          "body": "Bumps the minimum nom-exif requirement from \"3.4\" to \"3.4.1\" so\n`cargo install rexiftool --locked` and the pre-built GitHub Releases\nbinaries actually carry the Sony A7C2 HIF GPS fix (issue #50). Default\n`cargo install rexiftool` would already pull 3.4.1 via the caret\nrange, but locked / binary inst\n[…]\nxif lib tag for\nthe GitHub \"Latest release\" badge. (Today rexiftool-v0.2.0 wasn't\nlabelled Latest only because v3.4.0 outranks 0.2.0 in semver; future\nrexiftool 1.x could accidentally promote itself.)",
          "is_bot": false,
          "headline": "release(rexiftool): v0.2.1 — pick up nom-exif 3.4.1 GPS sub-IFD fix",
          "author_name": "min",
          "author_login": null,
          "committed_at": "2026-05-12T08:39:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "75163f937fff940eb722f99925fe55da0e3c987a",
          "body": "Patch release for #50: cameras that put GPSVersionID (tag 0) first in\nthe GPS sub-IFD (notably Sony A7C2 HIF) had every GPS field silently\ndropped because the IFD iterator's defensive `tag == 0` short-circuit\nfired on the legitimate version entry. The bail-out is now gated on\n\"not inside the GPS sub-IFD\"; the malformed-IFD defense survives\nelsewhere and parse_gps() recovers the full sub-IFD.\n\nPure bugfix, no API or behavior change for non-affected files.",
          "is_bot": false,
          "headline": "release: nom-exif v3.4.1 (2026-05-12) — Sony A7C2 HIF GPS fix",
          "author_name": "min",
          "author_login": null,
          "committed_at": "2026-05-12T08:24:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "476f24f16d3cac0a081a350610beefbeb43c7d7b",
          "body": "… trap\n\n`IfdHeaderTravel` currently only inspects `sub_ifd_offset`, so its\n`tag == 0` short-circuit is observationally inert. If the struct ever\nstarts emitting entry values, that short-circuit will replay the bug\nfixed in 921ca3f for `IfdIter`. Point the reader at the gated version\nand at issue #50.",
          "is_bot": false,
          "headline": "docs(travel): warn future maintainers about the tag==0 / GPSVersionID…",
          "author_name": "min",
          "author_login": null,
          "committed_at": "2026-05-12T08:16:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "921ca3fd2c4a0a60f29cb0bc44436d4582aaf26d",
          "body": "`IfdIter::parse_tag_entry` short-circuited on `tag == 0` as a defensive\nguard against zero-padded malformed IFDs. But tag 0 is also the\nlegitimate GPSVersionID — the spec-defined first entry of the GPS\nsub-IFD. Aborting iteration there caused the whole sub-IFD to be\ndropped, silently losing every GP\n[…]\n-out on `!self.is_gps_subifd()` so the defense survives in\nnon-GPS contexts while GPSVersionID parses normally. Adds a regression\ntest built from a hand-crafted minimal little-endian TIFF.\n\nFixes #50.",
          "is_bot": false,
          "headline": "fix(exif): parse GPS sub-IFD when GPSVersionID is its first entry",
          "author_name": "min",
          "author_login": null,
          "committed_at": "2026-05-12T08:13:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "20f2305f42972a3893d78e5ea86a9a907a205345",
          "body": "BREAKING for `serde` feature: structured per-variant Serialize for\nEntryValue. Previously Serialize delegated to Display, which (a)\ntruncated arrays after 8/3 elements with `...` (silently losing data)\nand (b) emitted opaque strings like \"175/100 (1.7500)\" for rationals.\nThe new shape:\n\n- Scalar num\n[…]\nr `Name[...]` form.\n\nTest fixtures (testdata/exif.jpg.txt, canon-r6.cr3.sorted.txt) updated\nto the new format.\n\nBump rexiftool's nom-exif dep to 3.4 so a published rexiftool 0.2.0\npulls the fixed lib.",
          "is_bot": false,
          "headline": "release: nom-exif v3.4.0 — structured serde + Display redesign",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T15:28:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c34a8262e08ada28c4b336e9cf89bcbe114a13dc",
          "body": "Bump rexiftool 0.1.0 → 0.2.0 and document the new flags / behavior:\n\n- `:` separator instead of `=>`\n- IFD1 (thumbnail) hidden by default; `--with-thumbnail` opts in\n- long values truncated by default; `--full` opts out\n\nJSON output is unchanged from v0.1.0 (always full, always includes\nIFD1).",
          "is_bot": false,
          "headline": "release(rexiftool): v0.2.0 — output cleanup, IFD1 hidden by default",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T15:05:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f3b0e3ca0224e4325a8952984991f1715b83294e",
          "body": "Behavior changes (default human-readable output only; --json unaffected):\n\n- Hide IFD1 (thumbnail) entries by default. They mostly duplicate IFD0\n  (XResolution, ExifImageWidth, ResolutionUnit, …), which made the\n  output look like it had buggy duplicate keys. Add `--with-thumbnail`\n  to opt back in\n[…]\n\n  Embedded Track / Format Metadata blocks visually separate.\n- Render empty string values as `(empty)` instead of a bare trailing\n  separator, so `OffsetTime: ` no longer looks like a render failure.",
          "is_bot": false,
          "headline": "rexiftool: cleaner human output — `:` separator, truncation, IFD1 hidden",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T15:02:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0fd122e080f6231dd6480e453840446fd6544c27",
          "body": "- Section headers all share a 48-char `-- Title ---` style; show\n  `-- EXIF --` only when an Embedded Track or Format Metadata\n  section also follows, so single-section output stays uncluttered.\n- Key column auto-sizes from the widest tag (clamped 32..=48) so\n  long MakerNote/Unknown keys no longer break alignment.\n- Directory mode prints `File: {path}` via `Path::display()` instead\n  of the debug-quoted `OsStr`, and reuses the same divider width.",
          "is_bot": false,
          "headline": "rexiftool: unify print format across sections",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:49:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "660e9143f4b21458c8703c296ffb4b463fa7fca1",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove AGENTS.md",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:43:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3980e646d5265a09a2fc8196226827b85cc47c21",
          "body": "Triggers on `v[0-9]*` tag push (excludes rexiftool-v* and other\nnon-semver tags). Extracts the matching `## nom-exif vX.Y.Z`\nsection from CHANGELOG.md as the release body, appends a footer\nlinking to the full changelog and crates.io page, and creates a\ndraft release via gh CLI. Manual review + publish required.\n\nSkip: cargo publish stays manual — crates.io is irreversible and\nrelease notes for marquee versions benefit from human polish.",
          "is_bot": false,
          "headline": "ci: auto-create draft GitHub release on lib v* tag push",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:35:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9f30863c35609b66ab101f7716e127f6dbe77385",
          "body": null,
          "is_bot": false,
          "headline": "docs: tighten Supported File Types into two-line summary",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:13:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18425ac2f92467668103cdc21ca92a1fe7740d4c",
          "body": null,
          "is_bot": false,
          "headline": "docs: list AVIF in README + lib.rs supported formats",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:11:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b134f59af020a41505c6a2ce60be1abce45abe1f",
          "body": null,
          "is_bot": false,
          "headline": "docs: trim README rexiftool section, defer to crate README",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T14:07:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "81c61cbefe8a26a5a44c690529479b9f26d38104",
          "body": "Promote examples/rexiftool.rs to a workspace member crate at\ncrates/rexiftool/ (v0.1.0). Users can now install the CLI via:\n\n  cargo install rexiftool\n\nThe lib's Cargo.toml drops clap and serde_json from dev-dependencies\n(they were CLI-only) and removes the [[example]] block. The CLI gets\nits own RE\n[…]\n tag\npush, cross-builds release binaries for macOS x86_64, macOS aarch64,\nLinux x86_64, and Windows x86_64; packages each with the LICENSE +\nREADME; and uploads them to the GitHub release.\n\nCloses #3.",
          "is_bot": false,
          "headline": "feat: split rexiftool into a standalone CLI crate",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T13:59:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9444ead8de88f23b666e89fa38f421f2618de9a0",
          "body": "- Move Motion Photos section ahead of in-memory / format-specific\n  bits in both README and lib.rs (it is the headline differentiator,\n  not an appendix).\n- Drop deprecation noise from user-facing docs (already in CHANGELOG):\n  the \"Migration note (v3.3+)\" block in README, the \"v3.0-style API\n  (dep\n[…]\n verbose\n  clone+into+map EXIF extraction since Quick Start already shows it.\n- Update lib.rs API surface to point at MediaSource::from_memory\n  instead of listing the deprecated *_from_bytes helpers.",
          "is_bot": false,
          "headline": "docs: restructure README + lib.rs around Motion Photos",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T13:42:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "918d71b1f6cedeeb55252be3927126fc3365d1e5",
          "body": null,
          "is_bot": false,
          "headline": "release: v3.3.0 (2026-05-10) — PNG support + source-input unification",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T13:12:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a7fad88db8bfed0cc79e0e5f44b6653c4a272c48",
          "body": "Sync parse_exif (commit 70768d2) and async parse_image_metadata_async\n(commit 51876d0) both pick up PNG-scoped EOF tolerance, but async\nparse_exif_async was missed when the async memory mode landed.\nEffect: a 117-byte text-only.png passed to parse_exif_async via\ntokio::fs::File would surface Unexpec\n[…]\na_async via seekable → Ok with format\n\nThese tests actually traverse fill_buf and the PNG EOF-tolerance\ngate, and would catch any future regression that drops the gate\nfrom one of the four call sites.",
          "is_bot": false,
          "headline": "fix(async): parse_exif_async streaming branch missing PNG EOF tolerance",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T13:04:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e7a3e7ae1ea4bc2495f5de235f984106306e9da2",
          "body": "The async-memory deferral noted in P0 task 0.4 is no longer relevant —\nAsyncMediaSource gained from_memory and the three parse_*_async\nmethods all accept memory-mode sources with the same zero-copy\nguarantee as sync.",
          "is_bot": false,
          "headline": "docs(changelog): document AsyncMediaSource::from_memory",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:56:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1436e019d2a4026df94cf28dfaa65dbc7e6e96c",
          "body": "8 new tests covering the parity with the sync memory-mode route:\n\n- AsyncMediaSource::from_memory accepts JPEG / MOV / rejects unknown\n- parse_exif_async on memory JPG returns the expected EXIF\n- parse_exif_async memory mode does not poison the recycle cache\n  (parser.state.cached_ptr_for_test() sta\n[…]\ninfo\n- parse_image_metadata_async on memory PNG (eXIf + tEXt) returns\n  both halves\n- parse_image_metadata_async on memory text-only.png exercises the\n  PNG-scoped EOF tolerance through the async path",
          "is_bot": false,
          "headline": "test(async): coverage for AsyncMediaSource::from_memory",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:56:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "51876d092a017617be9bd04dd58c63c2ae44dc1b",
          "body": "Mirrors the v3.3 sync work that was deferred for async in P0:\n\n- AsyncMediaSource gains a `memory: Option<bytes::Bytes>` field.\n- AsyncMediaSource<tokio::io::Empty>::from_memory(bytes) is the new\n  zero-copy constructor (analog of MediaSource::from_memory). Returns\n  AsyncMediaSource<tokio::io::Empt\n[…]\nEOF tolerance\n  the sync version has (fix 70768d2), so tEXt-only PNGs <128 bytes\n  parse correctly through the async path too.\n\nNo new dependencies; no public API removals. Tests in subsequent\ncommit.",
          "is_bot": false,
          "headline": "feat(async): AsyncMediaSource gains memory mode",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:54:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "479cc3324c1c4deec64adc1c2f74b8924ac133fe",
          "body": "Previously the image branch called parse_exif then re-opened the\nfile to call parse_image_metadata for the format-metadata section —\ntwo file opens and two passes through the parser per PNG. Use a\nsingle parse_image_metadata call (which also returns the EXIF iter\nfor non-PNG images via its non-PNG f\n[…]\nall existing tests; smoke-tested on\ntestdata/exif.png (EXIF + Format Metadata), testdata/text-only.png\n(Format Metadata only), testdata/exif.jpg (EXIF only),\ntestdata/exif.png --no-format (EXIF only).",
          "is_bot": false,
          "headline": "refactor(rexiftool): single parse_image_metadata call per image",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:34:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "70768d2ed80b0a9e91f48e69428d899bf5de37ce",
          "body": "parse_exif and parse_image_metadata previously swallowed\nUnexpectedEof from the initial fill_buf whenever the buffer was\nnon-empty. That widened the contract for every format: a 100-byte\ntruncated JPEG (whose mime-detection prefill emptied the reader)\nsilently returned ExifNotFound instead of Unexpe\n[…]\nnexpectedEof for\ntiny truncations.\n\nGate the EOF-swallow on `mime_img == Png` and add a regression test\nusing testdata/compatible-brands.heic (36 bytes) to lock in the\nstrict-EOF contract for non-PNG.",
          "is_bot": false,
          "headline": "fix(parser): scope EOF tolerance to PNG only",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:32:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "92c21ba1ff36cf00a7ce47d63f0a602e28ac8baf",
          "body": "extract_chunks computed `8 + length as usize + 4` per chunk, where\nlength is a u32 from the wire. On 32-bit targets this wraps when\nlength is near u32::MAX, producing a tiny `total` that passes the\nlength check and then advances `cursor` by a wrapped value — risk of\nre-parsing the same chunk forever or reading out of bounds.\n\nUse checked_add and bail with ParsingError::Failed on overflow.\nThe defensive test now also exercises the unknown-chunk path with\nu32::MAX length.",
          "is_bot": false,
          "headline": "fix(png): guard against chunk-length integer overflow on 32-bit",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T12:26:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d4acee09369576043aa55aa997e53faae704a34a",
          "body": "The return type Result<(Option<ExifIter>, Vec<(String,String)>),\ncrate::Error> triggers clippy::type_complexity. Added\n#[allow(clippy::type_complexity)] on both the sync and async variants\nso `cargo clippy -- -D warnings` passes cleanly.",
          "is_bot": false,
          "headline": "fix(clippy): suppress type_complexity on parse_png_full + async",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:59:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c45ede60f5cd5299b75ce163c364073a76e90f76",
          "body": "ExifNotFound (e.g. PNG with only tEXt chunks) was propagating as a\nhard error before format metadata could be printed. Now treat it as\nempty EXIF and continue to the format metadata section, so\ntext-only.png shows \"-- Format Metadata --\" without an EXIF section.",
          "is_bot": false,
          "headline": "fix(rexiftool): show Format Metadata even when image has no EXIF",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:58:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0d54383f79268dcb5a843eb7ce19480817e068b8",
          "body": "Three sections under Unreleased:\n- Added: PNG support, parse_image_metadata, from_memory, new\n  public types, rexiftool format-metadata printing\n- Deprecated: from_bytes / parse_*_from_bytes / read_*_from_bytes\n  family (removed in v4)\n- Notes: read_image_metadata top-level helpers deferred to v4;\n  iTXt/zTXt deferred (non-breaking when added)",
          "is_bot": false,
          "headline": "docs(changelog): v3.3 — PNG support + source-input unification",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:56:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e262650b6399e98412c4cfb64b4ff8ec40ee7e1f",
          "body": "Mirrors the existing -- Embedded Track -- pattern. Adds:\n- --no-format flag to suppress the new section\n- JSON dump nests text chunks under \"_format\" key\n\nThe text chunks come from MediaParser::parse_image_metadata's\nImageFormatMetadata::Png(PngTextChunks). For non-PNG inputs the\nsection is omitted entirely.",
          "is_bot": false,
          "headline": "feat(rexiftool): print PNG tEXt chunks under -- Format Metadata --",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:56:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1bfb257aa0c9ed2743c99ded2246d3e69d32359f",
          "body": "Adds the highlights bullet about PNG and a new \"Image metadata\nbeyond EXIF\" section with a runnable doctest example showing\nparse_image_metadata + ImageFormatMetadata::Png.",
          "is_bot": false,
          "headline": "docs: lib.rs module docstring — PNG + parse_image_metadata",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:54:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "30c39fab344f68372953bccb5e12792edcbc5546",
          "body": "Adds .png to \"Supported File Types\" and a new\n\"Format-Specific Metadata\" section demonstrating the\nparse_image_metadata entry point. The example shows EXIF +\nImageFormatMetadata::Png(PngTextChunks) extraction in one call.\n\nNotes that legacy EXIF (Raw profile type *) is transparently\nmerged, and that top-level read_image_metadata helpers are\ndeferred to v4.",
          "is_bot": false,
          "headline": "docs: README — PNG support + parse_image_metadata example",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:54:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fbd76fb8d5822f66aab71e1a145cc24b76704649",
          "body": "Three new fixtures:\n- exif-legacy.png: Raw profile type exif only (ImageMagick style)\n- exif-legacy-app1.png: Raw profile type APP1 with \"Exif\\0\\0\" prefix\n- exif-both.png: eXIf + Raw profile type exif (different content)\n\nTests verify:\n- read_exif transparently works on legacy-only PNGs\n- parse_image_metadata exposes the raw \"Raw profile type exif\"\n  tEXt entry alongside the merged EXIF\n- eXIf takes precedence when both are present",
          "is_bot": false,
          "headline": "test: integration tests for PNG legacy hex-encoded EXIF",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:51:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "600a400abe8c742b1c1b6bfd974c02005bd591ff",
          "body": "Replaces the P4 stub (which returned ExifNotFound for Legacy) with\na real implementation: wrap the owned Vec<u8> in a fresh\nbytes::Bytes and feed to input_into_iter.\n\nThe fresh allocation is acceptable because:\n- Legacy hex-encoded EXIF in PNG is rare in modern files (eXIf path\n  is preferred when both exist, and most encoders write eXIf).\n- Legacy TIFF blobs are typically small (a few KB).\n- The eXIf path remains zero-copy via the parser's share_buf.",
          "is_bot": false,
          "headline": "feat: materialize PngExifSource::Legacy into ExifIter",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:50:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "191d39a9cfeb3f3edda45fb41fee4934a618a5f1",
          "body": "ImageMagick \"Raw profile type exif\" and Photoshop \"Raw profile type\nAPP1\" tEXt entries are hex-decoded, APP1 prefix stripped, and\nvalidated as TIFF (must have valid byte-order marker + magic 42).\nOn success, surfaced as PngExifSource::Legacy(Vec<u8>).\n\nSource-priority rule:\n  3 (highest) — eXIf chun\n[…]\ns of file order.\nThe original tEXt entry is preserved in text_chunks regardless.\n\nFailed hex-decode / failed TIFF validation → silently drop the\nlegacy candidate (text_chunks still has the raw value).",
          "is_bot": false,
          "headline": "feat: extract_chunks recognizes legacy EXIF in tEXt chunks",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:49:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e1550a39a0a950ec6b6577e0e38b99eceb3bbb5e",
          "body": "Used in the next commit by extract_chunks to recognize legacy\nEXIF embedded in \"Raw profile type {exif,APP1}\" tEXt chunks\n(ImageMagick / Photoshop pattern).\n\ndecode_raw_profile_value handles the 3-line header preamble that\nImageMagick prepends; hex_decode is the underlying primitive\n(tolerates whitespace, validates length parity).",
          "is_bot": false,
          "headline": "feat: hex_decode + decode_raw_profile_value helpers",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:47:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6dbbfc85f6c8a55765abb5abda620025a0402f49",
          "body": "Generates two fixture PNGs programmatically (eXIf+tEXt and tEXt-only)\nusing a real EXIF blob extracted from testdata/exif.jpg's APP1\nsegment.\n\nTests cover:\n- read_exif on EXIF-bearing PNG (file path)\n- read_exif on tEXt-only PNG returns ExifNotFound (contract preserved)\n- parse_image_metadata on EXI\n[…]\n rejected with UnexpectedEof because header detection\nconsumed all bytes before the parse loop could read more.\n\nLegacy hex-encoded EXIF (Raw profile type *) is not yet covered;\nthat lands in phase 5.",
          "is_bot": false,
          "headline": "test: PNG integration tests for parse_image_metadata + read_exif",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:44:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6e232985b936c83493a031e5df16678d98ac4426",
          "body": "Mirrors the sync parse_image_metadata using AsyncBufParser. PNG\nchunk extraction reuses the same png::extract_chunks pure function\nvia the async load_and_parse, so no PNG-specific async code beyond\nthe dispatch wrapper.",
          "is_bot": false,
          "headline": "feat: parse_image_metadata_async (tokio)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:39:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5d9bde96849bdb4af02d62f3efdbe4c8f2df1c2e",
          "body": "Single method handles file/stream/memory sources via the same\n<R: Read> bound used by parse_exif. Internal dispatch:\n- PNG: parse_png_full producing (ExifIter, Vec<(String, String)>)\n- Other images: existing parse_exif_iter; format is None\n- Tracks: ExifNotFound (use parse_track)\n\nReturns ImageMetad\n[…]\nifIter>; convert via .into() for eager\nImageMetadata<Exif> if desired.\n\nFor PNG, the tEXt chunks become ImageFormatMetadata::Png(PngTextChunks).\nIf both exif and format are None, returns ExifNotFound.",
          "is_bot": false,
          "headline": "feat: MediaParser::parse_image_metadata (sync)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:38:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9f7454dac18fdfdf37ebef1d7f4feb1c9d6c2a03",
          "body": "Dispatched at the top of parse_exif_iter (peer to the existing CR3\nspecial case). For PNG inputs, calls png::extract_chunks via\nload_and_parse, then materializes PngExifSource::EXif(Range) into\nan ExifIter using the existing share_buf zero-copy path.\n\nLegacy path (PngExifSource::Legacy) is wired up \n[…]\ns the same shape using AsyncBufParser.\n\nThe MediaMimeImage::Png arm in extract_exif_with_mime is now\nunreachable (the top-level dispatch routes around it). Marked\nunreachable!() to make this explicit.",
          "is_bot": false,
          "headline": "feat: parse_png_exif_iter — PNG eXIf path integration",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:36:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3361d3989766ac87ea7cd9dda5e42d0e706b4a10",
          "body": "Programmatic PNG byte-stream builders: signature + chunks + IEND.\nAlso includes a helper to extract a real TIFF/EXIF blob from\ntestdata/exif.jpg's APP1 segment so we can build PNGs with real\nEXIF data without hand-crafting it.\n\nCRCs are zeroed — nom-exif's PNG parser doesn't verify CRCs\n(consistent with JPEG marker handling).\n\nThis module is shared across integration tests via\n`#[path = \"png_fixtures.rs\"] mod png_fixtures;` includes.",
          "is_bot": false,
          "headline": "test: add PNG fixture builders for integration tests",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:34:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5201538852838925ad71ac3e6d534300986fa55a",
          "body": "Replace forward-references to parse_image_metadata (which lands in P4)\nwith plain text to eliminate cargo doc warnings at this phase.",
          "is_bot": false,
          "headline": "docs: fix broken intra-doc links in image_metadata module",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:32:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "86c448988bbf7c12775964f69d658a4da590f45e",
          "body": "Shows the default (eager) form, explicit lazy form, and From\nconversion between them. Verifies type-level composition compiles\ncleanly without users needing to spell out the sealed trait.",
          "is_bot": false,
          "headline": "docs: add doctest demonstrating ImageMetadata generic forms",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:31:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "535d6c4210469becfa04b81a2a53f67cf9e00b64",
          "body": "Public types for the new parse_image_metadata entry point:\n- ImageMetadata<E: ExifRepr = Exif> — { exif, format } struct\n- ImageFormatMetadata — #[non_exhaustive] enum with Png variant\n- ExifRepr — sealed marker trait, impl'd by Exif and ExifIter\n- From<ImageMetadata<ExifIter>> for ImageMetadata<Exif> — eager\n  conversion\n\nPNG dispatch in MediaParser is still stubbed; the type can be\nconstructed but the parse_image_metadata method that returns it\nlands in phase 4.",
          "is_bot": false,
          "headline": "feat: add ImageMetadata<E>, ImageFormatMetadata, ExifRepr",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:30:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b88b8af70ac0aaa614b5b5aec26a068d3f825810",
          "body": "Opaque wrapper around Vec<(String, String)> exposing get / get_all /\niter / len / is_empty. Latin-1 decoded key/value pairs in PNG file\norder, duplicates preserved.\n\nWill be the payload of ImageFormatMetadata::Png in the next commit.",
          "is_bot": false,
          "headline": "feat: add PngTextChunks public type",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:29:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b946fc75b57add2b0a9d14accd6cca6554ddd897",
          "body": "Crafted inputs (oversized tEXt, u32::MAX length declaration) must\nnot allocate gigabytes or panic. Verifies the size-cap branches in\nthe tEXt arm trigger ClearAndSkip / Need without entering the\nallocation path.",
          "is_bot": false,
          "headline": "test: defensive bounds in extract_chunks",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:27:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "550226d657aadd4d4fb5e80478ce8488dcea3c54",
          "body": "Verifies:\n- Truncated inside eXIf body → ParsingError::Need\n- Truncated before unknown chunk body → ParsingError::ClearAndSkip\n  (large IDAT chunks are streaming-friendly: never enter the parse\n  buffer, the I/O driver seeks past them)",
          "is_bot": false,
          "headline": "test: streaming Need/Skip behavior of extract_chunks",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:27:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a83862574b3658536bcdc4ff9a69336e55bea21e",
          "body": "tEXt chunks split on the first 0x00 byte; both halves Latin-1\ndecoded into String (infallible — every Latin-1 byte maps to a\nUnicode code point). Duplicate keys preserved (PNG spec permits\nthis). Malformed tEXt (no NUL) silently skipped.\n\nDefensive bounds:\n- MAX_TEXT_CHUNK_SIZE = 1 MiB rejects overs\n[…]\n 16 MiB caps cumulative captured size.\n\nLegacy \"Raw profile type *\" recognition (hex-decode → EXIF blob)\ndeferred to phase 5. For now, those entries appear in text_chunks\nverbatim like any other tEXt.",
          "is_bot": false,
          "headline": "feat: extract_chunks captures tEXt key/value pairs",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:26:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8b0b778bb1845f084426fb9db06fcee30f01a932",
          "body": "When an eXIf chunk is encountered, captures its byte range as\nPngExifSource::EXif(Range). Range is into the input buffer — no\nallocation; the caller can use bytes::Bytes::slice for zero-copy.\n\neXIf has the highest priority: when both eXIf and a legacy\nRaw-profile-type tEXt are present, eXIf wins. Legacy handling\nlands in phase 5.",
          "is_bot": false,
          "headline": "feat: extract_chunks recognizes eXIf chunk",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:25:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "dfad46dec2742b7325254a75d5c08637aee7587c",
          "body": "Validates the PNG 8-byte signature, walks chunk headers, returns on\nIEND. Unknown chunks are skipped via ParsingError::ClearAndSkip\n(letting the I/O driver seek past them). No metadata extraction\nyet — eXIf and tEXt handling come in subsequent commits.",
          "is_bot": false,
          "headline": "feat: extract_chunks signature + header loop",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:25:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0ece3ebd95a3e830eadb17394acc6fb3d42c97b9",
          "body": "Empty module with the public-types shape that the chunk-parser\nfunction will populate. No actual parsing logic yet — that lands\nin subsequent commits in this phase.",
          "is_bot": false,
          "headline": "feat: scaffold src/png.rs module with PngParseOut + PngExifSource",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:24:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9c2270a8f12ac34bd48a1d13ea2c037a87d14cc8",
          "body": "Returns ParsingError::Failed at this stage. Phase 4 replaces this\nwith a special-cased dispatch at the top of parse_exif_iter (peer\nto the existing CR3 special case), so this arm is unreachable in\nthe final design. It exists now only to satisfy the exhaustiveness\ncheck in the match.",
          "is_bot": false,
          "headline": "feat: add PNG arm to extract_exif_with_mime (stub)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:22:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bef91c5db111c8b83e07367e1b2b9b17207cafd9",
          "body": "Adds MediaMimeImage::Png variant and signature check\n(\"\\x89PNG\\r\\n\\x1a\\n\") in MediaMime::try_from. The check sits after\nTIFF detection (defensive ordering — no actual collision exists\nsince PNG/TIFF signatures differ in the first byte).\n\nEXIF extraction for PNG is not yet wired up; that lands in phase 4.\nThis commit only makes the mime layer recognize the format.",
          "is_bot": false,
          "headline": "feat: detect PNG files by 8-byte signature",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:22:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "464199eb2d40c5d2ddacecb4f0584562a5cd0aa9",
          "body": "A 1x1 grayscale PNG with no metadata, used for mime-detection tests.\nReal metadata-bearing PNG fixtures (with eXIf and tEXt chunks) are\ngenerated programmatically by tests/png_fixtures.rs in later phases.\n\nCRCs in the placeholder are zeroed; nom-exif's PNG parser does not\nvalidate CRCs (consistent with how JPEG markers and HEIC boxes are\nhandled today).",
          "is_bot": false,
          "headline": "test: add minimal testdata/exif.png placeholder",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:21:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b231e3fb856e22e5d973737bd6ceb8b804c6c3dc",
          "body": "Documents the deprecation of MediaSource::<()>::from_bytes,\nparse_*_from_bytes, and read_*_from_bytes in favor of the unified\nMediaSource::from_memory + parse_* path. Includes the migration\ntable and a side-by-side code example.\n\nRemoval of the deprecated symbols is scheduled for v4.",
          "is_bot": false,
          "headline": "docs: add v3.0 → v3.3 migration subsection (in-memory bytes API)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:19:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "804e05a397433bdc4d69a399340e1b4db2c3691e",
          "body": "The In-Memory Bytes section now demonstrates the v3.3 unified API.\nAdds a Migration Note paragraph linking to docs/MIGRATION.md for\nexisting v3.0-3.2 users on the deprecated *_from_bytes path.",
          "is_bot": false,
          "headline": "docs: migrate README in-memory examples to from_memory",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:18:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea110df6e07ca9c39e11a759c85709ade243d402",
          "body": "The module-level docstring's \"Reading from in-memory bytes\" section\nnow demonstrates the v3.3 unified API. A short deprecation note at\nthe section's end points existing read_*_from_bytes users to the\nnew path.",
          "is_bot": false,
          "headline": "docs: migrate lib.rs in-memory examples to from_memory",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:17:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4eacc220c11c3900de4cfdb1aa6eeeeadf8c5d0a",
          "body": "read_exif_from_bytes, read_exif_iter_from_bytes,\nread_track_from_bytes, read_metadata_from_bytes are now deprecated\nand delegate internally to the unified MediaSource::from_memory +\nparse_* path. Removed in v4.",
          "is_bot": false,
          "headline": "deprecate: top-level read_*_from_bytes helpers",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:17:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a321aef6f07b5673ffdd914e1ea0034dee553299",
          "body": "The deprecated MediaSource::<()>::from_bytes and parse_*_from_bytes\nmethods are still exercised by the existing test suite (they need\nto keep working through v3.x). Annotate those specific tests with\n#[allow(deprecated)] so deprecation warnings don't pollute CI.\n\nUser-facing deprecation warnings (when callers use the deprecated\nmethods in their own code) are unaffected.",
          "is_bot": false,
          "headline": "test: silence deprecation warnings in tests of deprecated paths",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:15:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2dbe23a197f134614a502d379041dc0a49d46bcc",
          "body": "Marks the v3.0 memory-mode API as deprecated in favor of the unified\nv3.3 form (MediaSource::from_memory + parse_exif / parse_track).\n\nThe deprecated parse_*_from_bytes methods now delegate via the\nprivate into_empty() adapter, eliminating duplicate dispatch logic.\n\nWill be removed in v4. Tests still pass; deprecation warnings inside\nthe crate are addressed in a follow-up commit.",
          "is_bot": false,
          "headline": "deprecate: MediaSource::<()>::from_bytes and parse_*_from_bytes",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:13:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "af38fa1a234f1975994afe269d5ed69ea4c1039c",
          "body": "Mirror the sync refactor: runtime branch on memory.is_some(). The\nexisting async memory-mode entry points (if any) continue to work;\nnew memory-mode async sources can be parsed via the unified path\nonce AsyncMediaSource gains a from_memory constructor (deferred).\n\nNote: AsyncMediaSource has no memory field (no from_bytes constructor\nexists). Steps 2/3 of the plan are skipped per the explicit\ncontingency — async memory-mode is deferred to a follow-up.",
          "is_bot": false,
          "headline": "refactor: parse_exif_async / parse_track_async handle memory mode",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:12:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0304bc1ad082b5f5d370822b65fa121c10d41819",
          "body": "Same shape as the parse_exif refactor in the previous commit:\nruntime branch on ms.memory.is_some() picks the memory-mode (zero\ncopy) or streaming path. Memory-mode JPEG callers do not get\nMotion Photo extraction (matches existing parse_track_from_bytes\nbehavior).",
          "is_bot": false,
          "headline": "refactor: parse_track handles memory-mode sources internally",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:12:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db3344dd79e8182ae788eaef8e756a17b6e51b2a",
          "body": "parse_exif<R: Read> now branches on ms.memory.is_some() at runtime.\nMemory-mode sources (built via MediaSource::from_memory) take the\nsame zero-copy state.set_memory path that parse_exif_from_bytes uses\ntoday. Streaming sources are unchanged.\n\nExisting parse_exif_from_bytes still works (will be marked\ndeprecated in a later commit).",
          "is_bot": false,
          "headline": "refactor: parse_exif handles memory-mode sources internally",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T11:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c132977a960565f69134038f4a68029e4d02752b",
          "body": "Returns MediaSource<std::io::Empty>, which satisfies the existing\n<R: Read> bound on parse_exif / parse_track. This sets up the\nupcoming unification — a single parse_* method per \"what to parse\",\nwith no separate _from_bytes sibling.\n\nThe old MediaSource::<()>::from_bytes constructor stays untouched in\nthis commit; it will be deprecated in a follow-up commit once the\nparse_* methods are updated to handle memory-mode sources.",
          "is_bot": false,
          "headline": "feat: add MediaSource::from_memory for unified memory-mode source",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T06:06:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "87d933e878f5aeee67882c281b57901ec79eb1fe",
          "body": "Internal scaffolding for the upcoming source-input unification. Lets\nthe deprecated parse_*_from_bytes methods delegate to the unified\nparse_* methods once those are extended to handle memory mode.\n\nNo behavior change.",
          "is_bot": false,
          "headline": "refactor: add private MediaSource::<()>::into_empty() adapter",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T05:49:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7e8c907479ebf005be64c32b9807cfa95061f076",
          "body": "Master plan + per-phase plans for issue #18 (PNG support). Each\nphase plan is self-contained with bite-sized task steps, exact code\nsnippets, expected command output, and per-task atomic commits.\n\nMaster:\n  png-support-master.md  — index + cross-phase rules + risk register\n\nPhases:\n  P0: source-inpu\n[…]\netadata integration (6 tasks)\n  P5: legacy EXIF-in-tEXt (hex-decoded TIFF) (5 tasks)\n  P6: docs + CLI + changelog (5 tasks)\n\nReferences spec at docs/superpowers/specs/2026-05-10-png-support-design.md.",
          "is_bot": false,
          "headline": "docs: add PNG support implementation plan (7 phases)",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T05:29:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0b90cad0b5cab22a1dd12ed1f93e3a972fe9a276",
          "body": "Updates the PNG support spec to reflect three decisions:\n\n1. FormatMetadata renamed to ImageFormatMetadata. Track-side formats\n   (Matroska Chapters/Attachments, MP4 iTunes, ID3, etc.) are real\n   future candidates for format-specific extras; mixing them into a\n   single FormatMetadata enum would be\n[…]\n release.\n\nBuild sequence becomes 7 phases (0-6), with phase 6 explicitly\ncovering README, lib.rs module docs, doctests, MIGRATION.md, and\nCHANGELOG updates for both PNG and the deprecation migration.",
          "is_bot": false,
          "headline": "docs: bundle source-input unification into PNG PR",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T04:59:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fe72c522b4a5cdf335c30860c0ac98b8cb432568",
          "body": "Drops the three top-level helpers (read_image_metadata,\nread_image_metadata_from_bytes, read_image_metadata_async) from the\nv3.3 plan. Adding them now alongside the existing read_metadata would\ncreate two adjacent top-level entry points with overlapping-but-not-\nidentical semantics, which the v4 Met\n[…]\n the redesigned Metadata enum.\n\nUpdates Scope, public API listing, default-type-parameter rationale,\ntest layers, build-sequence phase 4 and 6, v4 forward-compat note, and\nopen-questions risk wording.",
          "is_bot": false,
          "headline": "docs: defer top-level read_image_metadata helpers to v4",
          "author_name": "Min Deng",
          "author_login": "mindeng",
          "committed_at": "2026-05-10T03:02:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 48,
      "commits_last_year": 274,
      "latest_release_at": "2026-06-11T14:36:21Z",
      "latest_release_tag": "nom-exif-v3.6.1",
      "releases_from_tags": false,
      "days_since_last_push": 1,
      "active_weeks_last_year": 11,
      "days_since_latest_release": 40,
      "mean_days_between_releases": 3.4
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 42,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "nom-exif",
          "exists": true,
          "license": "non-standard",
          "keywords": [
            "exif",
            "metadata",
            "multimedia::audio",
            "multimedia::images",
            "multimedia::video",
            "parsing",
            "parser-implementations"
          ],
          "ecosystem": "crates",
          "matches_repo": true,
          "registry_url": "https://crates.io/crates/nom-exif",
          "is_deprecated": false,
          "latest_version": "3.6.1",
          "repository_url": "https://github.com/mindeng/nom-exif",
          "versions_count": 55,
          "total_downloads": 155187,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 12569,
          "first_published_at": "2024-02-15T06:05:01.025567Z",
          "latest_published_at": "2026-07-20T03:26:04.072650Z",
          "latest_version_yanked": false,
          "days_since_latest_publish": 1
        }
      ]
    },
    "popularity": {
      "forks": 16,
      "stars": 117,
      "watchers": 2,
      "fork_history": {
        "days": [
          {
            "date": "2024-07-09",
            "count": 1
          },
          {
            "date": "2024-07-10",
            "count": 2
          },
          {
            "date": "2024-07-12",
            "count": 1
          },
          {
            "date": "2024-07-16",
            "count": 1
          },
          {
            "date": "2024-09-13",
            "count": 1
          },
          {
            "date": "2024-10-18",
            "count": 1
          },
          {
            "date": "2024-11-29",
            "count": 1
          },
          {
            "date": "2024-12-29",
            "count": 1
          },
          {
            "date": "2025-02-19",
            "count": 1
          },
          {
            "date": "2025-06-05",
            "count": 1
          },
          {
            "date": "2025-09-04",
            "count": 1
          },
          {
            "date": "2026-05-09",
            "count": 1
          },
          {
            "date": "2026-05-14",
            "count": 1
          },
          {
            "date": "2026-05-23",
            "count": 1
          },
          {
            "date": "2026-06-08",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 16,
        "total_forks": 16
      },
      "star_history": {
        "days": [
          {
            "date": "2024-07-04",
            "count": 1
          },
          {
            "date": "2024-07-06",
            "count": 1
          },
          {
            "date": "2024-07-10",
            "count": 10
          },
          {
            "date": "2024-07-11",
            "count": 4
          },
          {
            "date": "2024-07-13",
            "count": 3
          },
          {
            "date": "2024-07-14",
            "count": 1
          },
          {
            "date": "2024-07-15",
            "count": 1
          },
          {
            "date": "2024-07-16",
            "count": 3
          },
          {
            "date": "2024-07-22",
            "count": 6
          },
          {
            "date": "2024-07-31",
            "count": 1
          },
          {
            "date": "2024-08-09",
            "count": 1
          },
          {
            "date": "2024-08-10",
            "count": 1
          },
          {
            "date": "2024-09-04",
            "count": 1
          },
          {
            "date": "2024-09-13",
            "count": 2
          },
          {
            "date": "2024-09-18",
            "count": 4
          },
          {
            "date": "2024-09-19",
            "count": 1
          },
          {
            "date": "2024-09-20",
            "count": 1
          },
          {
            "date": "2024-10-02",
            "count": 1
          },
          {
            "date": "2024-10-10",
            "count": 1
          },
          {
            "date": "2024-10-21",
            "count": 1
          },
          {
            "date": "2024-10-24",
            "count": 1
          },
          {
            "date": "2024-11-11",
            "count": 1
          },
          {
            "date": "2024-11-17",
            "count": 2
          },
          {
            "date": "2024-11-28",
            "count": 1
          },
          {
            "date": "2024-11-29",
            "count": 1
          },
          {
            "date": "2024-12-06",
            "count": 1
          },
          {
            "date": "2024-12-07",
            "count": 1
          },
          {
            "date": "2024-12-24",
            "count": 1
          },
          {
            "date": "2024-12-29",
            "count": 2
          },
          {
            "date": "2025-01-30",
            "count": 1
          },
          {
            "date": "2025-01-31",
            "count": 1
          },
          {
            "date": "2025-02-07",
            "count": 1
          },
          {
            "date": "2025-03-01",
            "count": 2
          },
          {
            "date": "2025-03-08",
            "count": 2
          },
          {
            "date": "2025-03-11",
            "count": 1
          },
          {
            "date": "2025-04-04",
            "count": 1
          },
          {
            "date": "2025-04-09",
            "count": 1
          },
          {
            "date": "2025-05-07",
            "count": 1
          },
          {
            "date": "2025-06-22",
            "count": 1
          },
          {
            "date": "2025-06-27",
            "count": 1
          },
          {
            "date": "2025-07-02",
            "count": 1
          },
          {
            "date": "2025-07-05",
            "count": 1
          },
          {
            "date": "2025-07-14",
            "count": 1
          },
          {
            "date": "2025-07-19",
            "count": 1
          },
          {
            "date": "2025-08-04",
            "count": 1
          },
          {
            "date": "2025-08-20",
            "count": 1
          },
          {
            "date": "2025-08-22",
            "count": 1
          },
          {
            "date": "2025-08-27",
            "count": 1
          },
          {
            "date": "2025-09-25",
            "count": 2
          },
          {
            "date": "2025-12-05",
            "count": 1
          },
          {
            "date": "2025-12-08",
            "count": 1
          },
          {
            "date": "2025-12-27",
            "count": 1
          },
          {
            "date": "2026-01-15",
            "count": 1
          },
          {
            "date": "2026-01-19",
            "count": 1
          },
          {
            "date": "2026-01-27",
            "count": 2
          },
          {
            "date": "2026-01-31",
            "count": 1
          },
          {
            "date": "2026-02-06",
            "count": 1
          },
          {
            "date": "2026-02-09",
            "count": 1
          },
          {
            "date": "2026-02-16",
            "count": 1
          },
          {
            "date": "2026-02-28",
            "count": 1
          },
          {
            "date": "2026-04-11",
            "count": 1
          },
          {
            "date": "2026-04-13",
            "count": 1
          },
          {
            "date": "2026-04-26",
            "count": 1
          },
          {
            "date": "2026-05-09",
            "count": 2
          },
          {
            "date": "2026-05-10",
            "count": 7
          },
          {
            "date": "2026-05-11",
            "count": 1
          },
          {
            "date": "2026-05-12",
            "count": 1
          },
          {
            "date": "2026-05-13",
            "count": 2
          },
          {
            "date": "2026-05-19",
            "count": 2
          },
          {
            "date": "2026-05-28",
            "count": 2
          },
          {
            "date": "2026-06-09",
            "count": 1
          },
          {
            "date": "2026-06-13",
            "count": 1
          },
          {
            "date": "2026-06-20",
            "count": 1
          },
          {
            "date": "2026-06-23",
            "count": 1
          },
          {
            "date": "2026-06-30",
            "count": 1
          },
          {
            "date": "2026-07-07",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 117,
        "total_stars": 117
      },
      "open_issues_and_prs": 5
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "Cargo.toml",
        "crates/rexiftool/Cargo.toml",
        "fuzz/Cargo.toml"
      ],
      "largest_source_bytes": 87944,
      "source_files_sampled": 45,
      "oversized_source_files": 1,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "Cargo.toml",
        "fuzz/Cargo.toml"
      ],
      "advisories": {
        "error": "No resolved dependencies carried a version and a supported ecosystem",
        "scope": "repository_graph",
        "source": null,
        "findings": [],
        "collected": false,
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "assessed_package": null,
        "unassessed_count": 16,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "crates"
      ],
      "dependencies": [
        {
          "name": "nom",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "8.0"
        },
        {
          "name": "thiserror",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "2.0.11"
        },
        {
          "name": "serde",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "1.0"
        },
        {
          "name": "regex",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "1.10"
        },
        {
          "name": "chrono",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "0.4"
        },
        {
          "name": "tracing",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "0.1.40"
        },
        {
          "name": "tokio",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "1.40.0"
        },
        {
          "name": "bytes",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "1.7.1"
        },
        {
          "name": "iso6709parse",
          "manifest": "Cargo.toml",
          "ecosystem": "crates",
          "version_constraint": "0.1.0"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "bytes",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "chrono",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "iso6709parse",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "nom",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "regex",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "serde",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "thiserror",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "tokio",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "tracing",
            "direct": true,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "afl",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "clap",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "nom-exif",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "rand",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "serde_json",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "test-case",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          },
          {
            "name": "tracing-subscriber",
            "direct": false,
            "version": null,
            "ecosystem": "crates"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 16,
        "direct_count": 9,
        "indirect_count": 7
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 2,
        "merged_prs": 18,
        "open_issues": 3,
        "closed_ratio": 0.923,
        "closed_issues": 36,
        "closed_unmerged_prs": 2
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "mindeng",
          "commits": 456,
          "avatar_url": "https://avatars.githubusercontent.com/u/921932?v=4"
        },
        {
          "type": "User",
          "login": "rolandd",
          "commits": 12,
          "avatar_url": "https://avatars.githubusercontent.com/u/3670?v=4"
        },
        {
          "type": "User",
          "login": "xandkar",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/846873?v=4"
        },
        {
          "type": "User",
          "login": "onkoe",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/66580279?v=4"
        },
        {
          "type": "User",
          "login": "Enduriel",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/50383024?v=4"
        },
        {
          "type": "User",
          "login": "jayvdb",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/15092?v=4"
        },
        {
          "type": "User",
          "login": "mqudsi",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/606923?v=4"
        },
        {
          "type": "User",
          "login": "csarn",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/4563314?v=4"
        },
        {
          "type": "User",
          "login": "only1zf",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/213174?v=4"
        },
        {
          "type": "User",
          "login": "MarkusWendorf",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/18446907?v=4"
        }
      ],
      "contributors_sampled": 11,
      "top_contributor_share": 0.942
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "lib-release.yml",
        "rexiftool-release.yml",
        "rust.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 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": "5 out of 5 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": 1,
            "reason": "Found 5/30 approved changesets -- score normalized to 1",
            "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": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 10,
            "reason": "project is 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 9 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "d0b1412d6f115b256fdbc47ba71475f509186745",
        "ran_at": "2026-07-21T20:37:02Z",
        "aggregate_score": 4.3,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/mindeng/nom-exif",
    "host": "github.com",
    "name": "nom-exif",
    "owner": "mindeng"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 64,
      "inputs": {
        "security": 43,
        "vitality": 79,
        "community": 52,
        "governance": 61,
        "engineering": 81
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 79,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "commits_last_year": 274,
              "human_commit_share": 1,
              "days_since_last_push": 1,
              "active_weeks_last_year": 11
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 1 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 1
                    }
                  }
                ],
                "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": "274 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 274
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 9 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": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "releases_count": 48,
              "latest_release_tag": "nom-exif-v3.6.1",
              "releases_from_tags": false,
              "days_since_latest_release": 40,
              "mean_days_between_releases": 3.4
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "48 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 48
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 40 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 40
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~3.4 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 3.4
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 40,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 40 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 40
                    }
                  }
                ],
                "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": "at_risk",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 43,
            "inputs": {
              "forks": 16,
              "stars": 117,
              "watchers": 2,
              "growth_state": "organic",
              "growth_factor_pct": 100
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "117 stars",
                "points": 33.5,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 117
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "16 forks",
                "points": 9.8,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 16
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "2 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 68,
            "inputs": {
              "packages": [
                "nom-exif"
              ],
              "dependents": null,
              "ecosystems": "crates",
              "total_downloads": 155187,
              "monthly_downloads": 12569
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "12,569 downloads/month across crates",
                "points": 54.7,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 12569,
                      "ecosystems": "crates"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 61,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 24,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 11,
              "top_contributor_share": 0.942
            },
            "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 94% of commits",
                "points": 1.3,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 94
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "11 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 11
                    }
                  }
                ],
                "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": null,
            "notes": [],
            "value": 79,
            "inputs": {
              "merged_prs": 18,
              "open_issues": 3,
              "closed_issues": 36,
              "issue_closed_ratio": 0.923,
              "closed_unmerged_prs": 2
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "92% of issues closed",
                "points": 43.2,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 92
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "18/20 decided PRs merged",
                "points": 34.4,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 18,
                      "decided": 20
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 5/30 approved changesets -- score normalized to 1",
                "points": 1.5,
                "status": "partial",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "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": 58,
            "inputs": {
              "followers": 39,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "mindeng",
              "public_repos": 70,
              "account_age_days": 5482
            },
            "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": "39 followers of mindeng",
                "points": 11.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 39,
                      "login": "mindeng"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "70 public repos, account ~15 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 70
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 15
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "nom-exif"
              ],
              "ecosystems": "crates",
              "any_deprecated": false,
              "min_days_since_publish": 1
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on crates",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "crates"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 1 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 1
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "55 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 55
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 81,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 68,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "5 out of 5 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "excellent",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "exif",
                "heif",
                "jpeg",
                "metadata",
                "mp4",
                "nom",
                "parser",
                "quicktime",
                "rust",
                "matroska",
                "mkv",
                "webm",
                "mov",
                "raw",
                "tiff",
                "cr3"
              ],
              "has_wiki": true,
              "homepage": "https://crates.io/crates/nom-exif",
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://crates.io/crates/nom-exif",
                "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": "16 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 16
                    }
                  }
                ],
                "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": "at_risk",
        "name": "Security",
        "value": 43,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 43,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 17,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 1,
              "scorecard_aggregate": 4.3
            },
            "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": "5 out of 5 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 5/30 approved changesets -- score normalized to 1",
                "points": 0.8,
                "status": "partial",
                "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 dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is fuzzed",
                "points": 5,
                "status": "met",
                "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 9 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 21
            },
            "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": 53,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.98,
              "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": "98 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 98,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "at_risk",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 46,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [
                "Cargo.toml",
                "crates/rexiftool/Cargo.toml",
                "fuzz/Cargo.toml"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Cargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml (toolchain convention, no task runner)",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "toolchain_convention",
                    "params": {
                      "files": "Cargo.toml, crates/rexiftool/Cargo.toml, fuzz/Cargo.toml"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Rust (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Rust"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 99,
            "inputs": {
              "primary_language": "Rust",
              "largest_source_bytes": 87944,
              "source_files_sampled": 45,
              "oversized_source_files": 1
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Rust (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Rust"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "1/45 source files over 60KB",
                "points": 53.8,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 45,
                      "oversized": 1
                    }
                  }
                ],
                "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": [
    "No resolved dependencies carried a version and a supported ecosystem"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-21T20:37:19.007077Z",
  "schema_version": "0.23.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/m/mindeng/nom-exif.svg",
  "full_name": "mindeng/nom-exif",
  "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.23.0 — metodología completa · wiki de métricas.

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