Public record
Software health reportschema 0.27.0 · metrics 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 stars⑂ 1 forksince Feb 2026View on GitHub ↗

Averyy/wafer holds a health index of 51 out of 100, placing it in the Moderate band. It scores highest on Vitality (78/100) and lowest on Community & Adoption (29/100). It was last updated today. A single contributor accounts for most of its recent work.

51
overall / 100
Moderate

Software health index

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

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

Score profile

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

Ownership

Avery L.Personal account
9 followers13 public repossince Mar 2013

This repository is owned by a personal account. A single-owner project carries more continuity risk than an organization-backed one.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publishTags
PyPIwafer-py0.4.3-250 days agoanti-detectionclientfingerprinthttpscrapingtlswaf

Metrics by category

Vitality

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

78Good · 22% of overall
How it's scored
36/36Push recency — last push 0 days ago
8.3/36Commit cadence — 12/52 weeks with commits
16.5/18Commit volume — 68 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year68
human_commit_share1
days_since_last_push0
active_weeks_last_year12
How it's scored
16.2/27Ships releases — 24 version tags (no GitHub releases)
36/36Release recency — latest release 0 days ago
27/27Release cadence — a release every ~5.8 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count24
latest_release_tagv0.4.3
releases_from_tagsyes
days_since_latest_release0
mean_days_between_releases5.8
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

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

29Critical · 18% of overall
How it's scored
9.8/60Stars — 5 stars
0/25Forks — 1 forks
0/15Watchers — 0 watchers
Inputs used
forks1
stars5
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (Apache-2.0)
0/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
0/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno

Sustainability & Governance

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

41At risk · 24% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
0/22.5Commit distribution — top contributor authored 100% of commits
1.4/13.5Contributor breadth — 1 contributors
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Inputs used
bus_factor1
contributors_sampled1
top_contributor_share1
How it's scored
15.6/46.8Issue resolution — 33% of issues closed
0/38.3PR acceptance — no decided pull requests or no data
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Inputs used
merged_prs0
open_issues2
closed_issues1
issue_closed_ratio0.333
closed_unmerged_prs0
Excluded from scoring (no data or not applicable): PR acceptance. Remaining weights renormalized.
How it's scored
10/30Ownership backing — personal (user) account
0/20Verified domain — not applicable to user accounts
7.2/25Owner reach — 9 followers of Averyy
20.3/25Track record — 13 public repos, account ~13 yr old
Inputs used
followers9
owner_typeUser
is_verified
owner_loginAveryy
public_repos13
account_age_days4,883
Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.
How it's scored
25/25Published & resolvable — 1 package(s) on pypi
35/35Publish recency — latest publish 0 days ago
20/20Version history — 25 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packageswafer-py
ecosystemspypi
any_deprecatedno
min_days_since_publish0

Engineering Quality

Are baseline engineering and documentation practices in place?

62Moderate · 20% of overall
How it's scored
24/24CI workflows — 3 workflow(s)
24/24Tests present
0/16Linter config
0/9.6Pre-commit hooks
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — no data
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configno
has_precommit_configno
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.

Documentation

65Moderate
How it's scored
30/30README
25/25Documentation directory
0/15Documentation / homepage site
10/10Repository description
0/10Topics
0/10Wiki
Inputs used
topics
has_wikino
homepage
has_readmeyes
has_docs_diryes
has_descriptionyes

Security

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

39At risk · 16% of overall
How it's scored
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 — no data
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.5License — 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 — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 51 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3.9
Excluded from scoring (no data or not applicable): ci_tests, signed_releases. Remaining weights renormalized.

AI Readiness

How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score.

74Good · 0% of overall
How it's scored
45/45Agent instructions — CLAUDE.md
15/15Machine-readable docs (llms.txt) — llms.txt present
40/40Legible commit history — 64 of 71 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtyes
legible_history_share0.901
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes6,635
How it's scored
0/18One-command bootstrap
22/22Automated tests
0/11Lint / format config
11/11Static type checking — wafer/py.typed
10/10Reproducible environment — lockfile
0/10Demonstrated agent practice — no agent-authored commits among the last 71
0/8Automated maintenance — no automated dependency updates observed
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Inputs used
has_nixno
has_testsyes
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
How it's scored
27/45Type-checkable code — Python with type-check config (wafer/py.typed)
50.5/55Manageable file sizes — 8/97 source files over 60KB
Inputs used
primary_languagePython
largest_source_bytes395,436
source_files_sampled97
oversized_source_files8

Key facts

5GitHub stars
1contributors
68commits, last 12 months
0days since last push
24releases
1bus factor
2open issues
PyPIpackage ecosystems

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

More detail

OpenSSF Scorecard 3.9 / 10
3.9aggregate

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

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/aCI-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/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities51 existing vulnerabilities detected
Direct dependencies 1
RegistryPackageVersion constraintManifest
PyPIwreq>=0.12.1,<0.13pyproject.toml
All dependencies not collected

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

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

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

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

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