Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-29 22:48 UTC

Averyy / wafer

Anti-detection HTTP client for Python that handles TLS fingerprinting, WAF challenge solving, and cookie management.

PythonApache-2.0★ 5 estrellas⑂ 1 forkdesde feb 2026Ver en GitHub ↗

Averyy/wafer tiene un índice de salud de 51 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Vitality (78/100) y la más baja, Community & Adoption (29/100). Se actualizó por última vez hoy. Una sola persona concentra la mayor parte del trabajo reciente.

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

51
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

Avery L.Cuenta personal
9 seguidores13 repositorios públicosdesde mar 2013

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
PyPIwafer-py0.4.3-25hace 0 díasanti-detectionclientfingerprinthttpscrapingtlswaf

Métricas por categoría

Vitalidad

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

78Bueno · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
8.3/36Cadencia de commits — 12/52 semanas con commits
16.5/18Volumen de commits — 68 commits en el último año
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Datos de entrada utilizados
commits_last_year68
human_commit_share1
days_since_last_push0
active_weeks_last_year12
Cómo se puntúa
16.2/27Publica versiones — 24 etiquetas de versión (sin releases de GitHub)
36/36Recencia de las versiones — última versión hace 0 días
27/27Cadencia de publicación — una versión cada ~5,8 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count24
latest_release_tagv0.4.3
releases_from_tags
days_since_latest_release0
mean_days_between_releases5,8
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

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

29Crítico · 18% del índice global
Cómo se puntúa
9.8/60Estrellas — 5 estrellas
0/25Forks — 1 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks1
stars5
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (Apache-2.0)
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

Sostenibilidad y Gobernanza

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

41En riesgo · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
15.6/46.8Resolución de issues — 33% de issues cerradas
0/38.3Aceptación de PR — sin PR decididos o sin datos
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs0
open_issues2
closed_issues1
issue_closed_ratio0,333
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Aceptación de PR. Los pesos restantes se han renormalizado.
Cómo se puntúa
10/30Respaldo de la propiedad — cuenta personal (usuario)
0/20Dominio verificado — no aplicable a cuentas de usuario
7.2/25Alcance del propietario — 9 seguidores de Averyy
20.3/25Trayectoria — 13 repos públicos, cuenta de ~13 años
Datos de entrada utilizados
followers9
owner_typeUser
is_verified
owner_loginAveryy
public_repos13
account_age_days4883
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 pypi
35/35Recencia de publicación — última publicación hace 0 días
20/20Historial de versiones — 25 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packageswafer-py
ecosystemspypi
any_deprecatedno
min_days_since_publish0

Calidad de Ingeniería

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

62Moderado · 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
0/20OpenSSF Scorecard: CI-Tests — sin datos
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_configno
has_precommit_configno
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: CI-Tests. Los pesos restantes se han renormalizado.

Documentación

65Moderado
Cómo se puntúa
30/30README
25/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
0/10Topics
0/10Wiki
Datos de entrada utilizados
topics
has_wikino
homepage
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?

39En 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
0/2.5CI-Tests — sin datos
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
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
0/5Fuzzing — project is not fuzzed
2.5/2.5Licencia — license file detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
5/5Packaging — packaging workflow detected
5/5Pinned-Dependencies — all dependencies are pinned
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 51 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3,9
Excluidos de la puntuación (sin datos o no aplicable): ci_tests, signed_releases. Los pesos restantes se han renormalizado.

Preparación para IA

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

74Bueno · 0% del índice global
Cómo se puntúa
45/45Instrucciones para agentes — CLAUDE.md
15/15Documentación legible por máquinas (llms.txt) — llms.txt presente
40/40Historial de commits legible — 64 de 71 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txt
legible_history_share0,901
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes6635
Cómo se puntúa
0/18Arranque con un solo comando
22/22Pruebas automatizadas
0/11Configuración de lint / formato
11/11Verificación estática de tipos — wafer/py.typed
10/10Entorno reproducible — lockfile
0/10Práctica demostrada con agentes — ningún commit con autoría de agente entre los últimos 71
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Datos de entrada utilizados
has_nixno
has_tests
lockfilesuv.lock
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configswafer/py.typed
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
Cómo se puntúa
27/45Código verificable por tipos — Python con configuración de verificación de tipos (wafer/py.typed)
50.5/55Tamaños de archivo manejables — 8/97 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePython
largest_source_bytes395.436
source_files_sampled97
oversized_source_files8

Datos clave

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

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository
  • deps.dev does not index pypi:wafer-py@0.4.3; advisories assessed against the repository dependency graph instead

Más detalle

OpenSSF Scorecard 3.9 / 10
3.9agregado

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-29 22:48 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/dCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
10Packagingpackaging workflow detected
10Pinned-Dependenciesall dependencies are pinned
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
n/dSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities51 existing vulnerabilities detected
Dependencias directas 1
RegistroPaqueteRestricción de versiónManifiesto
PyPIwreq>=0.12.1,<0.13pyproject.toml
Todas las dependencias no recopilado

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

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 2945,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "CSS": 23694,
        "HTML": 87597,
        "Python": 2167872,
        "JavaScript": 100172
      },
      "pushed_at": "2026-07-29T22:45:14Z",
      "created_at": "2026-02-21T23:24:06Z",
      "owner_type": "User",
      "updated_at": "2026-07-29T22:45:12Z",
      "description": "Anti-detection HTTP client for Python that handles TLS fingerprinting, WAF challenge solving, and cookie management.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "Apache-2.0",
      "default_branch": "main",
      "license_spdx_raw": "Apache-2.0",
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": null,
      "name": "Avery L.",
      "type": "User",
      "login": "Averyy",
      "company": null,
      "location": "Canada",
      "followers": 9,
      "avatar_url": "https://avatars.githubusercontent.com/u/3880328?v=4",
      "created_at": "2013-03-16T02:50:43Z",
      "is_verified": null,
      "public_repos": 13,
      "account_age_days": 4883
    },
    "license": {
      "state": "standard",
      "spdx_id": "Apache-2.0",
      "raw_spdx": "Apache-2.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.4.3",
          "kind": "patch",
          "published_at": "2026-07-29T22:45:02Z"
        },
        {
          "tag": "v0.4.2",
          "kind": "patch",
          "published_at": "2026-07-29T14:39:49Z"
        },
        {
          "tag": "v0.4.1",
          "kind": "patch",
          "published_at": "2026-07-28T16:20:24Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2026-07-27T19:26:01Z"
        },
        {
          "tag": "v0.3.4",
          "kind": "patch",
          "published_at": "2026-07-26T15:48:52Z"
        },
        {
          "tag": "v0.3.3",
          "kind": "patch",
          "published_at": "2026-07-18T23:19:18Z"
        },
        {
          "tag": "v0.3.2",
          "kind": "patch",
          "published_at": "2026-07-18T14:06:42Z"
        },
        {
          "tag": "v0.3.1",
          "kind": "patch",
          "published_at": "2026-07-17T18:01:24Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-07-03T14:43:36Z"
        },
        {
          "tag": "v0.2.4",
          "kind": "patch",
          "published_at": "2026-06-07T19:59:18Z"
        },
        {
          "tag": "v0.2.3",
          "kind": "patch",
          "published_at": "2026-06-07T15:01:11Z"
        },
        {
          "tag": "v0.2.2",
          "kind": "patch",
          "published_at": "2026-06-06T20:50:18Z"
        },
        {
          "tag": "v0.2.1",
          "kind": "patch",
          "published_at": "2026-05-11T14:20:03Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-04-05T15:43:41Z"
        },
        {
          "tag": "v0.1.9",
          "kind": "patch",
          "published_at": "2026-03-06T17:30:48Z"
        },
        {
          "tag": "v0.1.8",
          "kind": "patch",
          "published_at": "2026-03-05T21:26:41Z"
        },
        {
          "tag": "v0.1.7",
          "kind": "patch",
          "published_at": "2026-03-02T20:31:57Z"
        },
        {
          "tag": "v0.1.6",
          "kind": "patch",
          "published_at": "2026-03-02T15:54:34Z"
        },
        {
          "tag": "v0.1.5",
          "kind": "patch",
          "published_at": "2026-02-27T18:14:27Z"
        },
        {
          "tag": "v0.1.4",
          "kind": "patch",
          "published_at": "2026-02-26T03:16:39Z"
        },
        {
          "tag": "v0.1.3",
          "kind": "patch",
          "published_at": "2026-02-25T14:33:24Z"
        },
        {
          "tag": "v0.1.2",
          "kind": "patch",
          "published_at": "2026-02-24T20:18:51Z"
        },
        {
          "tag": "v0.1.1",
          "kind": "patch",
          "published_at": "2026-02-24T17:53:52Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-02-23T23:30:40Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "b673126e7b5ff33c7e6fb76bbc993610750b92d0",
          "body": "Validate target DOM and distinguish browser passthrough from transport clearance.\n\nCorrect iframe geometry, render classification, and bounded solver cleanup. Add regression coverage and update the solver documentation.",
          "is_bot": false,
          "headline": "Fix Alibaba TMD browser solving",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-29T22:45:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c9c70775b807498286f342b435e95cb8cd293184",
          "body": "Fixed and released in v0.4.2. The findings it carried are preserved:\nairmatrix.ca is recorded in docs/site-list.md as the live regression\ntarget for cloudflare_block, and the block page itself is checked in at\ntests/fixtures/cloudflare_waf_block_1020.html.",
          "is_bot": false,
          "headline": "Remove the resolved Cloudflare WAF block TODO",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-29T14:55:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "219e410d586750dcaa74a8d2c692792180c3f72f",
          "body": "Three fixes from a crawler bug report against 0.4.1.\n\nsession.render(url) loads a page in the browser solver, waits for\nclient-side rendering to settle, and returns the finished document as a\nWaferResponse. Some pages ship a shell and write their content with\nJavaScript, so no fingerprint recovers m\n[…]\nd by the resolver, not\nunreachable, but the transport can only report a refused connection to\n[::]:443, which reads like a dead site. Resolved on the failure path\nonly, so the happy path pays nothing.",
          "is_bot": false,
          "headline": "Add browser-rendered fetch, terminal WAF blocks, DNS sinkhole diagnosis",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-29T14:39:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "305ee080f0fe4a1894d42509dd68e64817bf6dff",
          "body": "- add fixed-origin Reddit browser cookie recovery for sync and async sessions\n- return validated Cloudflare browser responses without pinning or rebuilding\n- expand regression coverage and update consumer and live-site documentation",
          "is_bot": false,
          "headline": "Fix Reddit recovery and Cloudflare passthrough",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-28T16:20:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7e255d276566985a48b96214aad0ddd7c509301e",
          "body": "Spot-checking the other solvers headless after the init-script fallback\nlanded: Cloudflare (200, 11.8s), Kasada (200/687KB, 12.7s) and Alibaba Baxia\n(18/18) all pass, DataDome does not -- ChallengeDetected, with\n_verify_headless_patches reporting outerWidth=1440 innerWidth=1440\ncolorDepth=24.\n\nThat \n[…]\nonditional, with the wellfound row\ncarrying the same caveat.\n\nHeaded was re-verified after the shared-path change and is unaffected:\nCloudflare 12.8s, DataDome 6.0s, Kasada 7.5s, Akamai 9.2s, all 200.",
          "is_bot": false,
          "headline": "docs: record that headless is fixed for some WAFs, not DataDome",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-27T19:26:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "69e6970eece12d5e20635289cb606d71448ac96e",
          "body": "…ation\n\nTesting drag replay headless showed the slider solving and earning no\ntarget-scoped x5sec three rounds running, ending in ChallengeDetected, where\nthe same burst headed returned real results.\n\nThe drag code was not at fault. Under Patchright with Chrome 150 on macOS,\nPage.addScriptToEvaluate\n[…]\nuture regression is loud instead of silent. The residual\nlimitation is documented: this lands just after document-start, so a WAF that\nfingerprints at document-start could still read pre-patch values.",
          "is_bot": false,
          "headline": "fix: make headless viable by re-applying fingerprint scripts on navig…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-27T18:53:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "26164f1c952b2929239b42b304c1bda55fa17c85",
          "body": "An API-vs-docs audit in both directions found two public session methods\nabsent from llms.txt. Both exist on SyncSession and AsyncSession, both need a\nbrowser_solver=, and both return bool meaning \"browser state was earned and\nimported\" -- not that the caller's application request succeeded, which i\n[…]\ne audit now reports clean in all four directions: no exported name or\npublic session method is undocumented, and no wafer.X or session.X symbol\ncited in llms.txt or README is missing from the package.",
          "is_bot": false,
          "headline": "docs: document browser_prime, browser_solve_challenge, and __version__",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-27T17:08:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5b3442649bdc2941178f4c3fd1b494b42a58d081",
          "body": "…lback\n\nFollow-up to the feature commit: four consumer-visible behaviours were\nchanged or added without matching docs.\n\n- get_cookie(): the RFC 6265 rewrite changed what it returns. llms.txt and\n  README still described \"exact-host first, then parent-domain\", which no\n  longer holds -- a host-only c\n[…]\nn-coordinate probe authorizes a toString-visible\n  override, so an inconclusive result must leave it off rather than abort.\n  Documents the real headless Chrome 150 geometry that made it inconclusive.",
          "is_bot": false,
          "headline": "docs: document cookie scoping, reCAPTCHA gating, and the screenxy fal…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-27T17:04:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3d5059104d3d478af73fa84d5367195c2319345a",
          "body": "Bundles six work streams that had accumulated uncommitted, plus fixes for\ndefects found reviewing and live-testing them.\n\nNew:\n- Profile.IOS_SAFARI: wire-verified iPhone Safari 26.5.2 TLS/H2/header\n  identity. JA3, JA4_b and JA4_c are recomputed from the module's own\n  constants in tests, which pins\n[…]\ng, Reddit bootstrap, Imperva, and AsyncSession\nincluding 4 concurrent coroutines. AWS WAF, F5 Shape and PX never\nchallenged, so their solvers are unexercised; DataDome is blocked by an\nIP/device flag.",
          "is_bot": false,
          "headline": "feat: iOS Safari profile, Chrome 150 identity pinning, solver hardening",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-27T16:48:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ade90aea8653e55c4a49b73b4c9c986a9c77fbe7",
          "body": null,
          "is_bot": false,
          "headline": "docs: record v0.3.4 release",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-26T15:51:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14959e81e5f0a74ad294221b8d1692bbf0b572ca",
          "body": "- validate and persist anonymous verification in sync and async sessions\n- cover concurrency, deadlines, response caps, and explicit Old Reddit behavior\n- fix encoded GET-form submissions for the Amazon inline solver",
          "is_bot": false,
          "headline": "fix: move Reddit bootstrap to New Reddit",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-26T15:48:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1d27b660b107a81e2edefc82e39fe53cf8905f48",
          "body": "- detect Reddit cold-session block pages and warm cookies via old Reddit\n- persist both cookie response legs and cover sync, async, and cache behavior",
          "is_bot": false,
          "headline": "fix: bootstrap Reddit JSON sessions",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-18T23:19:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "587ef0a0003b587254a16ece7f3b5d1d2e50d261",
          "body": "Patchright's Chromium (Chrome 150) is newer than wreq's newest Emulation\n(Chrome 149), so the post-solve pin block silently no-opped:\nemulation_for_version(150) returned None. WAF clearance cookies\n(cf_clearance, DataDome) bind to the solving browser's UA + client hints,\nso the Chrome149 replay was \n[…]\ninned wreq/native path. A\nWARNING now fires when the browser outruns wreq's newest profile (was silent).\n\nWire-verified on miata.net (Cloudflare): 200, clearance persists across\nrequests, 0 rotations.",
          "is_bot": false,
          "headline": "fix: align replay identity to solving browser after browser solve",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-18T14:06:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "41a44d96a74ea43cb946cb87c4aba1b54a4ed150",
          "body": "Add a resolve= session param (dict[str, list[str]]) so callers can hand wafer a pre-validated host->IP map. The socket connects to those IPs while TLS SNI and cert validation still key on the hostname, closing the TOCTOU DNS-rebinding window. Honored on both the wreq path (DnsOptions) and the native\n[…]\n and add real build numbers for Chrome148/149 and Edge148. Wire-verified Chrome149 sec-ch-ua, Safari H2, Dart HTTP/1.1, and tls_verify; full suite (1122 passed) plus a live regression smoke test pass.",
          "is_bot": false,
          "headline": "feat: SSRF-safe resolve= DNS pinning; bump wreq to 0.12.1 (Chrome149)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-17T18:01:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "337ef97b8244c5eb2213d54f8b858e8e468f6a45",
          "body": "- bulk()/max_rotations=0 now returns a persistent empty-200 response\n  instead of raising EmptyResponse (matches the 429/challenge gates and\n  the documented .bulk() contract)\n- Opera Mini honors the declared charset and returns body bytes exactly:\n  legacy-charset pages (windows-1251 etc.) decode c\n[…]\nllms.txt sync: 17 WAF types, DART profile, attempt_timeout and\n  max_response_size per-request kwargs, honest DataDome solver caps,\n  resp.history typing, broadened timeout guarantee to all transports",
          "is_bot": false,
          "headline": "fix: pre-release review fixes for v0.3.0",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-07-03T14:43:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "387f9cbc87487b1ae6a99d37953510e91e078b7f",
          "body": "The timeout= contract now explicitly covers backoff, rate-limit, and 429\nRetry-After waits (not just retries/rotations/solves) in llms.txt, the\nconstructor comments, Mistake #17, and the README rate-limit section -\nmatching the deadline-clamping fixes. A hostile Retry-After can't hold a\ncall past its deadline.",
          "is_bot": false,
          "headline": "docs: note total timeout also bounds rate-limit and Retry-After waits",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-15T12:21:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0af513b2d79124e00a4f1ecded49e26b59f39f4d",
          "body": "A wreq-layer timeout is now always bounded by the total timeout= budget and\nsurfaced as WaferTimeout (never ConnectionFailed). Make that explicit in the\nllms.txt raise/return lists and the README exhaustion table, distinguishing a\ngenuine connection error (refused/reset/TLS) from a hang.",
          "is_bot": false,
          "headline": "docs: document WaferTimeout on server hang past total timeout",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-14T14:09:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bf230975a793106f4ec583105902dbe814bf31e2",
          "body": "A code review of 5ea9141 (timeout= as a total budget) surfaced a cluster of\ndeadline-bypass bugs that the always-set deadline made reachable. Fixes:\n\n- Exception classification: every attempt now carries a timeout kwarg (the\n  remaining-budget clamp, even with no attempt_timeout), so any wreq\n  Time\n[…]\nss-\nretries (real sleeps consume the budget; the shared-time.sleep-patch trap is\navoided). 1075 pass, ruff clean. sync/async kept mirrored; re-reviewed by\ncorrectness, security, and regression agents.",
          "is_bot": false,
          "headline": "fix: close deadline-bypass holes exposed by the total-budget timeout",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-14T14:08:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5ea9141314ec2138e6053ed005f8f5aa4fe92d3b",
          "body": "Previously a per-request timeout= was the total call deadline, but a\nsession-level timeout= was only the per-attempt default (the wreq client\ntimeout) and did NOT cap total time across retries/rotations - so\nSyncSession(timeout=30) could run several times over. Now timeout= is the\nTOTAL budget for t\n[…]\nevel timeout= now caps total time. A\nmulti-retry or browser-solve flow that relied on the old per-attempt semantics\nshould raise its timeout= and/or set attempt_timeout=.\n\n1071 tests pass, ruff clean.",
          "is_bot": false,
          "headline": "feat: unify timeout= to a total budget (session and per-request)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-14T13:23:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ecd06a18047d820ffafef8f69127aff61cb8b63d",
          "body": "It was the only test in the default suite making a live third-party call\n(httpbin.org/headers), so an httpbin hiccup could fail the whole suite. Marked\n@pytest.mark.live + skipif(WAFER_LIVE != \"1\"), matching the reCAPTCHA live-test\nconvention. The default `pytest tests/` now has zero network dependencies; run\nthe live ones with WAFER_LIVE=1.",
          "is_bot": false,
          "headline": "test: gate the httpbin sec-ch-ua test behind @pytest.mark.live",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-14T12:59:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c2fb4af8375e238b2881d1c007aff14965cfca05",
          "body": "Closes the SessionPool TODO loose end. The type stays unbuilt (correctly -\nflagged not-recommended); document the recipe instead. fetchaller reinvents an\nidentical lazy double-checked-locked singleton in 9+ site modules and its\ngeneric fetch spins a throwaway session per URL. Mistake #14 warned against\nrecreating sessions but never showed the reuse pattern - now it does: sync +\nasync lazy singleton, plus the host-keyed dict[host, session] variant for a\ngeneric fetcher hitting many hosts.",
          "is_bot": false,
          "headline": "docs: add session-reuse recipe to llms.txt (Mistake #14)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T23:28:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be66c9e38e368186fdcc87c47ab7f8b65ecfc959",
          "body": "README.md was badly stale on the branch's new surface; filled the existing\nsections in place:\n- Response API: resp.cookies / resp.history / resp.emulation / get_cookie;\n  resp.content is true wire bytes; charset-aware resp.text.\n- Session Configuration: attempt_timeout, fingerprint_pool, solve_origi\n[…]\n-> q=0.7 Accept value; xhr-jquery header details.\n\nllms.txt audited against the code - already accurate, left unchanged.\nVerified no em dashes, signatures match code, fences balanced. 1072 tests pass.",
          "is_bot": false,
          "headline": "docs: sync README + ref-docs to the new API",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T19:11:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f3fa04b846ef4696864603b2969fccad18d49736",
          "body": "A 4-reviewer Opus pass (correctness/security/regression/architecture) over the\nfull branch surfaced these, now fixed:\n\n- max_response_size is enforced on EVERY transport, not just the wreq path. The\n  native-TLS (Imperva) and Opera Mini paths now bound their wire read AND their\n  decompressor, so a \n[…]\nts)\n  instead of returning a dangling 3xx.\n- Docs: README documents mint_recaptcha_v3; llms.txt import block gains\n  emulation_is_mobile; compute_co docstring corrected.\n\n1071 tests (+36), ruff clean.",
          "is_bot": false,
          "headline": "Address team code-review findings",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T14:13:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "de16f232614e4ae50847918def7dfde91706cfbc",
          "body": "…-lite\n\nE9 - max_response_size= (session + per-request): caps the response body and\nraises ResponseTooLarge(url, size, limit). wreq streaming is used to abort early\nonce the cap is exceeded (the oversize body is never fully buffered); a\nContent-Length over the cap short-circuits before the read. Def\n[…]\nhost-matching (closing the Phase 1\nTODO: a Domain=co.uk cookie no longer over-matches a sibling). Reserved\nlocalhost is exempt. Degrades to TLD+1 on a miss.\n\n1035 tests (+63 over Phase 7), ruff clean.",
          "is_bot": false,
          "headline": "Phase 8: max_response_size, async solver entry, Shape tightening, PSL…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T04:39:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "665d352c4b58904d064ec1575dbce8619a4293a1",
          "body": "…iles\n\nE4 - embed=\"xhr-jquery\": like embed=\"xhr\" (fetch/CORS emulation) but adds\nX-Requested-With: XMLHttpRequest and the jQuery Accept (application/json,\ntext/javascript, */*; q=0.01) at the client level, for legacy $.ajax/XHR\nendpoints. Two consumers hand-rolled this. Plain xhr/iframe unchanged.\n\n\n[…]\n_envelope() reports\nis_mobile. wreq has no mobile Chromium profile, so no sec-ch-ua-mobile is\ninvented. Wire-verified SafariIos26_2 (iPhone UA, Safari JA4, no sec-ch-ua).\n\n972 tests (+21), ruff clean.",
          "is_bot": false,
          "headline": "Phase 7: jQuery-XHR embed mode, generalized solve_origin, mobile prof…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T04:13:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3016510950b83e080e8f0353978716c574439558",
          "body": "Add session.mint_recaptcha_v3(sitekey, action, *, origin=None, referer=None,\nv=None, enterprise=False) on SyncSession + AsyncSession - mints a v3 score token\nover pure HTTP via Google's anchor/reload endpoints using the session's own\nTLS-emulated client (no browser). Ported from bell-fibre-map's wor\n[…]\nfer mints the\ntoken, it can't guarantee the site's threshold passes. Distinct from the existing\nbrowser-based reCAPTCHA v2 grid solver.\n\n951 tests (+42), 1 live test gated on WAFER_LIVE=1, ruff clean.",
          "is_bot": false,
          "headline": "Phase 6: browser-free reCAPTCHA v3 token minting",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T03:49:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "67a5a575f11a1f150d4adc39909fc3b122e12d25",
          "body": "…signal\n\n#2 - rotation now escalates ACROSS families before cycling versions. The ladder\nis Chrome -> Firefox -> Safari -> Edge -> Chrome-versions, and every family\nswitch swaps the matching header envelope (Phase 4's family_headers) so the TLS\nidentity and headers stay coherent (wire-verified each \n[…]\nrotation entry points (403, 429, empty-200, attempt-timeout) go through one\n_advance_rotation path so cross-family + pool + header coherence apply uniformly.\n\n909 tests (+19 over Phase 4), ruff clean.",
          "is_bot": false,
          "headline": "Phase 5: cross-family rotation ladder + fingerprint pool + empty-200 …",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T03:30:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e733224c0c97f2d25877b7a41d5e464b5648ade5",
          "body": "…esp.emulation\n\n#1 - non-Chrome family support above the TLS line:\n- Classify any wreq Emulation into a family (chrome/edge/firefox/opera/safari),\n  including FirefoxAndroid/FirefoxPrivate/SafariIos/SafariIPad variants.\n- Per-family HTTP header envelope when emulation= is set and headers= isn't:\n  F\n[…]\nome_headers now always holds the true Chrome envelope so a rotated non-Chrome\nsession stays coherent. Wire-verified Firefox149 + Edge147 against tls.peet.ws.\n\n890 tests (+53 over Phase 3), ruff clean.",
          "is_bot": false,
          "headline": "Phase 4: family-aware fingerprints (Firefox/Edge), public envelope, r…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T02:54:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c7f143467c131944e038815abdf609cfe5f3422b",
          "body": "Security:\n- Cookie files already land 0o600 via mkstemp; make that explicit (fchmod,\n  POSIX-guarded) so the owner-only guarantee survives future refactors, and\n  create the cache dir 0o700 so other local users can't enumerate which\n  domains are cached (the files were already unreadable, the dir wa\n[…]\nme coverage that isn't there.\n- llms.txt: same per-type honesty + DataDome added to the browser-required\n  list; note cookie files are 0o600 / cache dir 0o700.\n\n837 tests (+2 perms tests), ruff clean.",
          "is_bot": false,
          "headline": "Phase 3: harden cookie-cache perms + honest per-type solver-status docs",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T02:16:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ab20004e14c966252d3caf368d80d5e7f033c424",
          "body": "E1 - split per-attempt timeout from the total deadline:\n- New attempt_timeout= (session default + per-request override), distinct\n  from timeout=. Each attempt's wreq timeout is bounded by attempt_timeout\n  (clamped to the remaining total budget when a per-request timeout deadline\n  is also set). A \n[…]\nare always handed a\nsolver via browser_solver=, so a shared solver is no longer destroyed when\none session's context manager exits. Callers can use with/async with again.\n\n835 tests (+29), ruff clean.",
          "is_bot": false,
          "headline": "Phase 2: attempt_timeout (per-try bound) + solver-ownership lifecycle",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T02:04:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9a3e0bfdc1724486b119d603f51c71fe941558d5",
          "body": "… e.response)\n\nAdd requests/httpx-parity to WaferResponse and the challenge exceptions:\n\n- resp.text is now charset-aware: Content-Type charset -> <meta charset>\n  sniff (HTML bodies only) -> utf-8. Decoding moved off wreq's text() onto\n  the raw decompressed bytes (resp.bytes()), so resp.content is\n[…]\nt() return individual Set-Cookie values\n(5-tuple) so multi-cookie responses aren't collapsed; also fixes an Opera\nMini duplicate-header last-wins overwrite.\n\n806 tests (+72 over baseline), ruff clean.",
          "is_bot": false,
          "headline": "Phase 1: response & exception parity (charset text, cookies, history,…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-13T01:37:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3f490c047317d6182f412f5dda27138a73db3ef8",
          "body": "… host\n\nThe browser challenge-solver navigated top-level straight to an API host\n(e.g. api2.realtor.ca), which Imperva answers with its interactive\n\"Error 15\" block - a request shape no real browser produces. wait_for_imperva\nthen polled forever for a cookie that a top-level API nav never sets, so t\n[…]\ne on the derived embedder; skip IP hosts\n\nVerified live through fetchaller's realtor flow (geocode + search, GET + POST)\nand reviewed by correctness/regression/architecture/security agents. 734 tests.",
          "is_bot": false,
          "headline": "Fix Imperva \"Error 15\": browser-solve on the origin page, not the API…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T19:59:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "93a11d4f6c9a57ad421ed9ecb281a39f3dbc0d39",
          "body": "Clears the \"Node.js 20 actions are deprecated\" warning on the v4.2.2 pin.\nv6.0.3 runs on node24. Updated the SHA pin in ci.yml and both publish.yml jobs.",
          "is_bot": false,
          "headline": "ci: bump actions/checkout to v6.0.3 (Node 24)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T15:51:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c63f556b48ba5cf3be19548acfd4c83024f4ea8d",
          "body": "Now that Imperva is fully solved (native-TLS no-browser bypass for light usage,\nbrowser-solve reese84 for heavy usage), bring all docs in line:\n\n- README.md: new \"Imperva / Incapsula (no-browser bypass)\" section, updated the\n  challenge-escalation steps, added _native_tls.py to the file layout.\n- ll\n[…]\nst.md: api2.realtor.ca entry now covers light (12/12 no-browser)\n  + heavy (18/18 browser-solve) and the http.client transport.\n- todo-realtorsolve.md: heavy usage is solved, no longer an open caveat.",
          "is_bot": false,
          "headline": "docs: document the complete Imperva solution",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T15:01:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e344f3ffe810318a117512db0e0c2ae190c49d78",
          "body": "Fix C (commit 9961237) made a *pinned* host go straight to the browser solve\non the heavy reese84 state, but the *unpinned* trigger path -the common case-\nstill fell through and burned fingerprint rotations (Safari->Chrome, all\nBoringSSL and all re-challenged, plus session-retirement noise) before r\n[…]\nwser solve firing with no preceding\n\"Rotation: switched\"/\"Session retired\" log line (vs the rotation that showed up\nbefore this fix). Test: test_async_trigger_native_fail_reaches_browser_not_rotation.",
          "is_bot": false,
          "headline": "Skip pointless rotations on the unpinned Imperva path too",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T14:33:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "99612372f494da8f38b4873f9bb72462f2e20313",
          "body": "Correctness:\n- Skip the native path for socks/https proxies (it can't CONNECT-tunnel them):\n  new _native_tls_usable() gates the trigger, so those sessions go straight to\n  the proxy-aware wreq path instead of wasting 4 native attempts then failing.\n- Reset native_attempted/native_retries on a cross\n[…]\n+ docs/ref-imperva.md (skip, not\n  raise-then-fall-back).\n- Stripped em dashes from todo-realtorsolve.md / todo-wellfoundsolve.md.\n- Commented the was_retried=True and no-browser-exhaustion contracts.",
          "is_bot": false,
          "headline": "Address second-round self-review findings (3 reviewers)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T14:17:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f492c3ba74075385914ec0af544c40d5f1ebed47",
          "body": "Under heavy load Imperva revokes the OpenSSL free pass and demands the reese84\nJS token from everyone. The unpinned path already escalated to the browser\nsolve (which earns reese84; wreq then carries it through, verified live 18/18).\nBut a host pinned to the native path would exhaust its native retr\n[…]\nbrowser path\n- tests: test_async_sticky_exhausted_falls_back_to_browser_path (+ the existing\n  no-browser exhaustion test still raises)\n- llms.txt / docs/ref-imperva.md: corrected heavy-usage behavior",
          "is_bot": false,
          "headline": "Bulletproof Imperva heavy-usage: pinned host falls back to browser solve",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T13:53:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ceb2bd759859982d7e03c3f1c171c119ffd0889c",
          "body": "Correctness + consistency fixes from a two-reviewer self-review (no behavior\nchange for the realtor happy path; verified live 200 + 714 tests green):\n\n- Trigger success now calls rate_limiter.record(domain), matching the sticky\n  path (was skipped, so spacing wasn't enforced before the next native c\n[…]\na\n  pinned host's retries; document the proxy behavior.\n- Add docs/ref-imperva.md (one ref doc per WAF type, per CLAUDE.md).\n\nAsync and sync native blocks verified byte-identical (modulo await/sleep).",
          "is_bot": false,
          "headline": "Address self-review findings on native-TLS Imperva fallback",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T03:15:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5e31f5d4ba4b73ac471ccaf4ba16b17ef3b8c534",
          "body": "Firsthand during the full-review pass: appdev.pwc.com now returns the\nIncapsula JS interstitial (200, ~3.3KB with _Incapsula_Resource), correctly\ndetected as imperva by the 0.2.2 200-interstitial logic. curl gets the same\nchallenge page, so the OpenSSL native fallback correctly does not pin it and\nfalls through to browser-solve. Not a regression from the native-TLS change.",
          "is_bot": false,
          "headline": "site-list: appdev.pwc.com now serves Imperva interstitial (was TLS pass)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T02:36:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a66e4cb3c916c864f90fd4fcad4c063f609618ee",
          "body": "Verified firsthand 2/2 live that wafer returns the real SSR document for all\nfour wellfound page types: /jobs passes no-browser; /role, /company, /jobs/{id}\nhit white-labeled DataDome (ddm.wellfound.com) and pass via the existing browser\nsolver's passive passthrough. No code change needed.\n\nRecords \n[…]\ngested -those scripts are on every wellfound page\nincluding the successful SSR /jobs 200, so it would false-positive into an\ninfinite solve loop (same class as the Imperva _Incapsula_Resource marker).",
          "is_bot": false,
          "headline": "Document wellfound.com: already solved by existing DataDome solver",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T01:28:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a78ee6f16ac3485457e5747521afa88f44b38864",
          "body": "api2.realtor.ca fingerprints the TLS stack itself: every wreq/BoringSSL\nprofile (Chrome/Safari/OkHttp, H1/H2) is challenged regardless of cookies,\nwhile a generic OpenSSL client sending the minimal header set (no Sec-Fetch-*,\nno Accept-Language/Encoding) gets a free pass. wreq can't produce an OpenS\n[…]\nicky routing\n- wafer/_fingerprint.py: host_user_agent() for the native path\n- tests/test_native_tls.py: 18 tests; conftest wiring\n- llms.txt, docs/site-list.md, todo-realtorsolve.md: docs + resolution",
          "is_bot": false,
          "headline": "Add native-TLS (OpenSSL) fallback for Imperva TLS-fingerprinting sites",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-07T00:24:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "94366001b4a45eb22a6aba74fe78d19019231cf0",
          "body": "…olve timeout\n\nwreq 0.11.3 -> 0.12.0. The only Python-API change is ResolverOptions ->\nDnsOptions, which wafer does not use. Chrome147 is still the newest\nPython-exposed Emulation profile (Chrome148 exists only at the Rust\nlevel), so DEFAULT_EMULATION is unchanged. Wire-verified post-bump:\nChrome/Sa\n[…]\nitive guard) and for solve-timeout clamping (sync + async +\nlock-busy). Docs: site-list.md (realtor.ca, wellfound), llms.txt (timeout\n/ solver concurrency), README/CLAUDE.md/ref-wreq.md version notes.",
          "is_bot": false,
          "headline": "Bump wreq to 0.12.0; detect Imperva 200 interstitial; bound browser-s…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-06-06T20:50:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "550de0a149af93a181a1d0f56fd4efa621c2c3e3",
          "body": "Upstream wreq 0.11.3 ships the KeyShare registration fix (PR #582) that\nblocked us on 0.11. This bump adopts the v0.11 API renames:\n\n- Client kwarg: verify -> tls_verify (silently dropped pre-rename;\n  badssl confirmed our CertStore would have been a no-op)\n- TlsOptions: key_shares_limit=int -> key_\n[…]\nested vs\nnowsecure.nl (CF max-security) -200 on first try, no challenge.\n\nDocs note the Client-level silent-acceptance footgun (extends prior\nTlsOptions/Http2Options note) and the repr() shape change.",
          "is_bot": false,
          "headline": "Bump wreq to 0.11.3, default to Chrome147",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-05-11T14:20:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "46eb339b8948f0ca4c0cb7a7e5223eab860ae51a",
          "body": "Patchright 1.58.0 -> 1.59.1: Chromium 147, stabilized init script\ninjection, upstream Playwright bug fixes. Other lockfile bumps:\nonnxruntime 1.24.2 -> 1.25.1, pillow 12.1.1 -> 12.2.0,\nhuggingface_hub 1.4.1 -> 1.13.0, ruff 0.15.1 -> 0.15.12,\npytest 9.0.2 -> 9.0.3, datasets 4.8.4 -> 4.8.5.\n\nwreq held at <0.11 pending upstream KeyShare fix\n(merged in PR #582, awaiting next PyPI release).",
          "is_bot": false,
          "headline": "Bump patchright to 1.59.1, refresh dep lockfile",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-05-03T16:25:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "67a48c331f4b9da0c4f6b5df0d198cb2a7437864",
          "body": null,
          "is_bot": false,
          "headline": "Gitignore Claude session state directory",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-05-03T16:25:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4ab7a411cb56bdaeccdcc935db28a7439ea972d6",
          "body": "- Dependency: rnet>=3.0.0rc22,<4 -> wreq>=0.10.2,<0.11\n- All imports: from rnet -> from wreq across 12 source + 6 test files\n- Rename docs/ref-rnet.md -> docs/ref-wreq.md\n- Update llms.txt, README.md, CLAUDE.md references\n- Add migration notice for users upgrading from rnet\n- Tighten version pin to <0.11 (pre-1.0 semver caution)",
          "is_bot": false,
          "headline": "Migrate from rnet to wreq (renamed upstream)",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-04-05T15:43:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fb1680e53a972311c97573bc31aac78036e87a5d",
          "body": "Wire-verified identical to real Dart SDK 3.11.4 (JA3 hash\n203503b7023848ab87b9836c336b8e81). HTTP/1.1 forced via\nalpn_protocols=[] in TlsOptions, not http1_only (which injects\nan ALPN extension). First published Dart/Flutter TLS profile\nin any impersonation library.",
          "is_bot": false,
          "headline": "Add Dart 3.11 TLS profile for Flutter/Dart app impersonation",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-28T17:05:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "998e8129b30a08ce89ed5af5a84afe4992bc1f72",
          "body": "- Migrate DET annotations to HF-compatible metadata.jsonl (file_name field with subdir prefix)\n- Upload all training data to two private HF repos as Parquet (labeled + unlabeled)\n- Add dataset cards and upload runbook (training/recaptcha/huggingface-upload.md)\n- Add frozen dataset hash caching (.has\n[…]\n(10-16h) instead of fixed schedule\n- Use cached phash from metadata when available, skip redundant PIL open\n- Deduplicate CLAUDE.md rules already in global config\n- Add datasets and Pillow to dev deps",
          "is_bot": false,
          "headline": "Add HuggingFace dataset backup, improve collector dedup caching",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-27T21:19:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a8faaee88605a2b094d58411999641b4862a63af",
          "body": "… block detection\n\n- Remove --disable-site-isolation-trials flags (CF Turnstile detects and refuses to resolve)\n- Rewrite CF solver: _page_is_challenge() helper, 5s click throttle, managed+interactive support\n- Add patch_frame_screenxy() for per-frame CDP screenXY fix (DD, CF, hCaptcha, reCAPTCHA, B\n[…]\narkers to passthrough filters, expand search range to [:10000]\n- Detect F5 Shape soft-block pages (siteclosed/invitation redirect)\n- Add researchgate, chewy search/product to smoke tests and site list",
          "is_bot": false,
          "headline": "Fix CF Turnstile solver, per-frame screenXY/headless injection, Shape…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-06T17:30:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3916971e110a60411219fb3cdf6be008fd905a68",
          "body": "- Skip _HEADLESS_FIX_SCRIPT for Akamai (behavioral JS detects toString wrapper)\n- Add Akamai behavioral challenge auto-resolve detection (page content growth)\n- Reduce Kasada settle time 10s->3s (CT/ST arrive in 2-3s, rest was waste)\n- Remove DD audio/slider/slide-right solvers (DD rejects correct answers via CDP input detection)\n- Add early bail-out when DD escalates beyond WASM PoW\n- Add docs/ref-datadome.md documenting what works vs what doesn't",
          "is_bot": false,
          "headline": "Fix headless Chewy solve, remove broken DD interactive solvers",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-05T21:26:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e9ceee17807e4f8ebe5de646c1a6d3a42f4caf86",
          "body": null,
          "is_bot": false,
          "headline": "Move collector to training/recaptcha/, fix dedup threading bugs",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-04T16:16:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1650e75c0e19abc0d8e2e3ea464a4bd74de73731",
          "body": "…rprint pinning\n\nDataDome: audio captcha solver using faster-whisper (tiny/int8). Transcribes\n6 spoken digits, preferred over slider when available. Added faster-whisper\nto [browser] extra.\n\nKasada: fix headless detection by skipping _HEADLESS_FIX_SCRIPT (ips.js\ndetects Function.prototype.toString w\n[…]\nes from being\nsent with a different identity after rotation.\n\nHeadless: add scrgb-linear to launch args, conditional init script skip per\nchallenge type, new ref-headless.md documenting all overrides.",
          "is_bot": false,
          "headline": "Add DD audio captcha solver, fix headless Kasada detection, fix finge…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-04T13:44:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4d77cae8c60026d7e8285a32d5068121e8509cb7",
          "body": "Kasada (and other WAFs) instantly reject browsers with \"HeadlessChrome\"\nin the user-agent. Probe the raw UA at launch and replace it with\n\"Chrome\" so every headless context passes WAF fingerprinting.",
          "is_bot": false,
          "headline": "Fix headless browser leaking HeadlessChrome in UA string",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-02T20:31:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f257763702abbc63640b7c1bd333de849abb0848",
          "body": "… refs\n\n- llms.txt: Replace verbose Kasada/reCAPTCHA sections with generic\n  passthrough note, update DataDome solver list, fix result.extras comment\n- README: Fix challenge count (16->17, add GeeTest), fix max_rotations\n  default (1->2), fix kwargs list (data->body), add DD slide-right\n- ref-kasada\n[…]\nh \"next step\" (ST capture already done, blocker is H HMAC),\n  update Integration status re: disabled CT+CD injection\n- site-list.md: Add DD slide-right to description+summary, fix stale\n  Twitch entry",
          "is_bot": false,
          "headline": "Update docs for v0.1.6: Kasada passthrough, DD slide-right, fix stale…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-02T16:22:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c4e86c96bb400822c87214b234daf846d69184a2",
          "body": "…ookie TTL\n\n- Rewrite generate_cd() with correct hash chaining (challenge_id, sequential nonces, chain between subchallenges)\n- Disable CT+CD header injection (requires H HMAC; sending without H causes server rejection)\n- Add Kasada passthrough for dual-WAF sites where cookie replay fails (Chewy: Ak\n[…]\nl-WAF)\n- Add DataDome slide-right challenge solver (new \"slide right to secure\" variant, no canvas)\n- Fix browser-solve session cookies getting dropped (expires=-1 → 0.0 → cache eviction; now 24h TTL)",
          "is_bot": false,
          "headline": "Rewrite Kasada CD algorithm, add DD slide-right solver, fix session c…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-02T15:54:34Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "012e1a05b766e854284222fb503e91fee58266db",
          "body": "- Add training/recaptcha/ scripts: train_mps.py, export.py, dedup.py, predict_cls.py, collect.py\n- Update Mousse reviewer UI (recorder, styles, server, static assets)\n- Update reCAPTCHA grid solver and ref-models docs for new dataset layout\n- Move training data gitignore rules to nested training/recaptcha/.gitignore",
          "is_bot": false,
          "headline": "Add training scripts, update Mousse UI and reCAPTCHA grid solver",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-03-01T03:47:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a4cfb069e0420d9751b3a07adabcd6d124f907ec",
          "body": "…n up errors and Mousse UI\n\n- reCAPTCHA grid solver: swap YOLO for EfficientNet-B0 (3x3 CLS) + D-FINE (4x4 DET) via HuggingFace ONNX\n- Cookie cache: replace fcntl file locks with threading locks and atomic temp-file writes\n- Remove SessionBlocked error class, add WaferHTTPError\n- Mousse: add DET/CLS\n[…]\nA training data\n- Add docs/ref-models.md and docs/ref-recaptcha.md\n- Delete wafer/browser/models/__init__.py (models now downloaded from HuggingFace)\n- Update README, llms.txt, and pyproject.toml deps",
          "is_bot": false,
          "headline": "Replace YOLO with EfficientNet+D-FINE, fix cookie cache locking, clea…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-27T18:14:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6143d5423caa91cd1bb453e07864beb6b4f87b80",
          "body": "Challenge detection, cookie cache poisoning, solver dispatch parity,\nbrowser solver error handling, Imperva reese84, Opera Mini isolation,\nfingerprint build numbers, embed header duplication. Added tests.",
          "is_bot": false,
          "headline": "Fix bugs found during full codebase audit",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-26T03:16:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aef1c14204cd46374f4e7d6b429a6127fb43f5e0",
          "body": "- Remove response cookie write-through (was poisoning fresh TLS sessions\n  with stale WAF-bound cookies)\n- Add fresh-session-first rotation: 403 now rebuilds TLS before switching\n  to Safari, clearing domain cookies to avoid rehydrating tainted ones\n- Bump max_rotations default 1 -> 2 to budget fres\n[…]\nhen patchright is not installed\n- Fix embed mode: Sec-Fetch-Site computed dynamically, Accept set at\n  client level to avoid HTTP/2 duplication\n- Update tests, README, llms.txt, and ref-sec-fetch docs",
          "is_bot": false,
          "headline": "Fix cookie cache poisoning, rotation strategy, and BrowserSolver import",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-25T14:33:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0de8f830e2d20c01b8ac1f81658a0109d44b79ac",
          "body": "- Remove reCAPTCHA/hCaptcha body detection on HTTP 200 (false positives\n  on pages with login forms like old.reddit.com)\n- Rotation fallback switches Chrome to Safari instead of cycling Chrome\n  versions (fundamentally different TLS/H2 fingerprint)\n- max_rotations default 10 -> 1 (one rotation attempt, then give up)\n- Rotation delay = rate limiter interval + 1s (replaces aggressive 0.5s\n  exponential backoff that hammered sites)",
          "is_bot": false,
          "headline": "Fix false-positive challenge detection, add Safari rotation fallback",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-24T20:18:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6233cde19f76f2f55186a57002fba49dadcf305a",
          "body": "…ixes\n\n- Replace audio-based reCAPTCHA solver with image grid solver (3x3 classification + 4x4 COCO detection); gracefully skips when ONNX models absent\n- Add 45 recorded grid mouse hop paths for natural cell navigation\n- Add Mousse grid-specific short-hop recording mode\n- Add llms.txt consumer impl\n[…]\nnce docs\n- Fix cookie management, challenge detection, and async/sync response wrappers\n- Update WAF reference docs (Baxia, GeeTest, Kasada, PX, rnet)\n- Update site list with latest smoke test results",
          "is_bot": false,
          "headline": "Add reCAPTCHA image grid solver, Mousse recorder improvements, core f…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-24T17:53:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b488416f7644b43d74750738499a92ea9f1eb10e",
          "body": "hatch-vcs saw untracked artifacts from install/test steps, producing\na local version (0.1.1.dev0+g...) that PyPI rejects. Publish job now\ngets its own pristine checkout.",
          "is_bot": false,
          "headline": "Fix publish: split into test + publish jobs to avoid dirty tree",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T23:30:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1b57f60dbb6c11620d57b299389b2ee6ceb0023f",
          "body": null,
          "is_bot": false,
          "headline": "Fix publish: enable fetch-tags for hatch-vcs version derivation",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T23:20:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4191fc0335327dda17cd8375e85117b1a6dacd0e",
          "body": "…workflows",
          "is_bot": false,
          "headline": "Fix publish: use version tag for Docker-based pypi-publish, align CI …",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T23:07:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cbb812759964c58a10bfd0a0cd1fcdfd5e6c2b71",
          "body": null,
          "is_bot": false,
          "headline": "Fix CI: pin actions, install browser extra, run all tests",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T22:08:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4b79008f42dfc5f52bea5603b6412e590ee765b2",
          "body": null,
          "is_bot": false,
          "headline": "Harden PyPI packaging: pin CI actions, add py.typed, cap rnet dep",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T21:08:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "014c61fb141c6c2a8b9512a9cc524ab133820d43",
          "body": "…r, JS-only fast-fail\n\n- Safari 26 M3/M4 profile (custom TlsOptions + Http2Options, safari_locale param)\n- hCaptcha checkbox and reCAPTCHA v2 checkbox browser solvers\n- DataDome puzzle slider solver (CV notch detection + mousse drag replay)\n- JS-only challenges fast-fail without browser solver inste\n[…]\nename solver docs to docs/ref-*.md, add docs/ref-rnet.md with full API gotchas\n- Trim CLAUDE.md, update README with Safari docs and new challenge types\n- Add audio optional dependency (faster-whisper)",
          "is_bot": false,
          "headline": "Add Safari profile, hCaptcha/reCAPTCHA solvers, DataDome puzzle slide…",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T19:25:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8a85ee219ac1bd8c72cdd520bf03eb045cd3fd5a",
          "body": "- Replace live httpbin.org session tests with mocked rnet tests (no network in CI)\n- Install .[dev,browser] in CI so test_cv.py can import cv2\n- Remove --ignore flags from pytest command\n- Fix cookie cache load() to prune expired entries from disk instead of leaving them",
          "is_bot": false,
          "headline": "Fix CI: mock session tests, install browser extra, prune expired cookies",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-23T03:21:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1b52273e382c3e0a872cfe8ed959297b2329a2eb",
          "body": "- GeeTest v4 slide CAPTCHA solver (CV notch detection + drag replay, 12/12 on demo)\n- Baxia/AliExpress NoCaptcha slider solver (stealth injection, TMD flow, slide replay)\n- Opera Mini profile: stdlib urllib transport, no rnet — for Google/DDG SSR results\n- Public API: export all errors, Profile, DEF\n[…]\necompression\n- GeeTest detection: size guard against false positives on large pages\n- Cookie cache: auto-sweep expired domain files\n- Docs: press-and-hold.md → perimeterx.md, add baxia.md + geetest.md",
          "is_bot": false,
          "headline": "Add GeeTest, Baxia, and Opera Mini solvers; harden public API",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-22T23:29:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fbc78a592b62b53ee8a46a7a9f7420b9693d64b8",
          "body": null,
          "is_bot": false,
          "headline": "Fix CI: create venv before installing deps",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-22T04:23:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c33db1bbc338d582a728f76b39025944a66c58d3",
          "body": "Anti-detection HTTP client wrapping rnet with TLS fingerprinting,\nWAF challenge detection/solving (14 types), cookie caching, retry\nwith rotation, rate limiting, embed mode, and browser solving.",
          "is_bot": false,
          "headline": "Initial release",
          "author_name": "Avery Levitt",
          "author_login": "Averyy",
          "committed_at": "2026-02-22T04:21:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 24,
      "commits_last_year": 68,
      "latest_release_at": "2026-07-29T22:45:02Z",
      "latest_release_tag": "v0.4.3",
      "releases_from_tags": true,
      "days_since_last_push": 0,
      "active_weeks_last_year": 12,
      "days_since_latest_release": 0,
      "mean_days_between_releases": 5.8
    },
    "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": "wafer-py",
          "exists": true,
          "license": "Apache-2.0",
          "keywords": [
            "anti-detection",
            "client",
            "fingerprint",
            "http",
            "scraping",
            "tls",
            "waf",
            "Development Status :: 4 - Beta",
            "Intended Audience :: Developers",
            "License :: OSI Approved :: Apache Software License",
            "Programming Language :: Python :: 3",
            "Programming Language :: Python :: 3.12",
            "Programming Language :: Python :: 3.13",
            "Topic :: Internet :: WWW/HTTP"
          ],
          "ecosystem": "pypi",
          "matches_repo": true,
          "registry_url": "https://pypi.org/project/wafer-py/",
          "is_deprecated": false,
          "latest_version": "0.4.3",
          "repository_url": "https://github.com/Averyy/wafer",
          "versions_count": 25,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": "2026-02-19T18:46:56.363731Z",
          "latest_published_at": "2026-07-29T22:46:58.891785Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 0
        }
      ]
    },
    "popularity": {
      "forks": 1,
      "stars": 5,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2026-06-05",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 1,
        "total_forks": 1
      },
      "star_history": null,
      "open_issues_and_prs": 2
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": true,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "wafer/py.typed"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 395436,
      "source_files_sampled": 97,
      "oversized_source_files": 8,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 6635
    },
    "dependencies": {
      "manifests": [
        "pyproject.toml"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "wreq",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.12.1,<0.13"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 2,
        "closed_ratio": 0.333,
        "closed_issues": 1,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "Averyy",
          "commits": 71,
          "avatar_url": "https://avatars.githubusercontent.com/u/3880328?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "publish.yml",
        "training.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "uv.lock"
      ],
      "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": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 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": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 10,
            "reason": "all dependencies are pinned",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 0,
            "reason": "51 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "b673126e7b5ff33c7e6fb76bbc993610750b92d0",
        "ran_at": "2026-07-29T22:48:24Z",
        "aggregate_score": 3.9,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-29T22:47:02Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 1,
          "created_at": "2026-06-05T04:09:17Z",
          "last_comment_at": "2026-06-05T05:30:28Z",
          "last_comment_author": "shauneccles"
        },
        {
          "number": 3,
          "created_at": "2026-06-05T05:29:59Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/Averyy/wafer",
    "host": "github.com",
    "name": "wafer",
    "owner": "Averyy"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 51,
      "inputs": {
        "security": 39,
        "vitality": 78,
        "community": 29,
        "governance": 41,
        "engineering": 62
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 78,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "commits_last_year": 68,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 12
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "12/52 weeks with commits",
                "points": 8.3,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 12
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "68 commits in the last year",
                "points": 16.5,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 68
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 88,
            "inputs": {
              "releases_count": 24,
              "latest_release_tag": "v0.4.3",
              "releases_from_tags": true,
              "days_since_latest_release": 0,
              "mean_days_between_releases": 5.8
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "24 version tags (no GitHub releases)",
                "points": 16.2,
                "status": "partial",
                "details": [
                  {
                    "code": "version_tags_no_releases",
                    "params": {
                      "count": 24
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~5.8 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 5.8
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "critical",
        "name": "Community & Adoption",
        "value": 29,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "forks": 1,
              "stars": 5,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "5 stars",
                "points": 9.8,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "1 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "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 (Apache-2.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "Apache-2.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 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
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 41,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 25,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 2,
              "closed_issues": 1,
              "issue_closed_ratio": 0.333,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "33% of issues closed",
                "points": 15.6,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 33
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 47,
            "inputs": {
              "followers": 9,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "Averyy",
              "public_repos": 13,
              "account_age_days": 4883
            },
            "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": "9 followers of Averyy",
                "points": 7.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 9,
                      "login": "Averyy"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "13 public repos, account ~13 yr old",
                "points": 20.3,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 13
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 13
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "wafer-py"
              ],
              "ecosystems": "pypi",
              "any_deprecated": false,
              "min_days_since_publish": 0
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on pypi",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 0 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "25 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 25
                    }
                  }
                ],
                "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": "moderate",
        "name": "Engineering Quality",
        "value": 62,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 60,
            "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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 65,
            "inputs": {
              "topics": [],
              "has_wiki": false,
              "homepage": null,
              "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 39,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 39,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.9
            },
            "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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no 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 not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "51 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 2
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "good",
        "name": "AI Readiness",
        "value": 74,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "has_llms_txt": true,
              "legible_history_share": 0.901,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 6635
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": "llms.txt present",
                "points": 15,
                "status": "met",
                "details": [
                  {
                    "code": "llms_txt_present",
                    "params": {}
                  }
                ],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "64 of 71 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 64,
                      "sampled": 71
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 53,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "uv.lock"
              ],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [
                "wafer/py.typed"
              ],
              "agent_commit_share": 0,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "wafer/py.typed",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "wafer/py.typed"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 71",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 71
                    }
                  }
                ],
                "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": "all dependencies are pinned",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "good",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 78,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 395436,
              "source_files_sampled": 97,
              "oversized_source_files": 8
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (wafer/py.typed)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "wafer/py.typed",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "8/97 source files over 60KB",
                "points": 50.5,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 97,
                      "oversized": 8
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
    "deps.dev does not index pypi:wafer-py@0.4.3; advisories assessed against the repository dependency graph instead"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-29T22:48:32.090849Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/a/Averyy/wafer.svg",
  "full_name": "Averyy/wafer",
  "license_state": "standard",
  "license_spdx": "Apache-2.0"
}

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

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

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