Public record
Software health reportschema 0.27.0 · metrics 1.13.0 · 2026-07-28 02:48 UTC

anatolykoptev / go-enriche

GoMIT★ 0 stars⑂ 0 forkssince Mar 2026View on GitHub ↗

anatolykoptev/go-enriche holds a health index of 59 out of 100, placing it in the Moderate band. It scores highest on Vitality (84/100) and lowest on Community & Adoption (24/100). It was last updated 1 day ago. A single contributor accounts for most of its recent work.

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

59
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

Anatoly KoptevPersonal account
14 followers94 public repossince Oct 2025oxpulse.chat

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 publish
Gogithub.com/anatolykoptev/go-enrichev1.40.3-729 days ago

Metrics by category

Vitality

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

84Good · 22% of overall
How it's scored
36/36Push recency — last push 1 days ago
10.4/36Commit cadence — 15/52 weeks with commits
18/18Commit volume — 151 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year151
human_commit_share1
days_since_last_push1
active_weeks_last_year15

Release discipline

100Excellent
How it's scored
27/27Ships releases — 7 releases published
36/36Release recency — latest release 18 days ago
27/27Release cadence — a release every ~21.3 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count7
latest_release_tagv1.40.0
releases_from_tagsno
days_since_latest_release18
mean_days_between_releases21.3
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?

24Critical · 18% of overall
How it's scored
0/60Stars — 0 stars
0/25Forks — 0 forks
0/15Watchers — 0 watchers
Inputs used
forks0
stars0
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (MIT)
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?

50Moderate · 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
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
Inputs used
bus_factor1
contributors_sampled1
top_contributor_share1
How it's scored
26.7/46.8Issue resolution — 57% of issues closed
37.6/38.3PR acceptance — 57/58 decided PRs merged
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Inputs used
merged_prs57
open_issues3
closed_issues4
issue_closed_ratio0.571
closed_unmerged_prs1
How it's scored
10/30Ownership backing — personal (user) account
0/20Verified domain — not applicable to user accounts
8.5/25Owner reach — 14 followers of anatolykoptev
14.5/25Track record — 94 public repos, account ~0 yr old
Inputs used
followers14
owner_typeUser
is_verified
owner_loginanatolykoptev
public_repos94
account_age_days276
Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.
How it's scored
25/25Published & resolvable — 1 package(s) on go
35/35Publish recency — latest publish 9 days ago
20/20Version history — 72 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagesgithub.com/anatolykoptev/go-enriche
ecosystemsgo
any_deprecatedno
min_days_since_publish9

Engineering Quality

Are baseline engineering and documentation practices in place?

82Good · 20% of overall
How it's scored
24/24CI workflows — 1 workflow(s)
24/24Tests present
16/16Linter config — .golangci.yml
9.6/9.6Pre-commit hooks
0/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 2 out of 2 merged PRs checked by a CI test -- score normalized to 10
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configyes

Documentation

65Moderate
How it's scored
30/30README
25/25Documentation directory
0/15Documentation / homepage site
0/10Repository description
10/10Topics — 5 topics
0/10Wiki
Inputs used
topicsdata-enrichment, go, golang, mcp, mcp-server
has_wikino
homepage
has_readmeyes
has_docs_diryes
has_descriptionno

Security

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

48At 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
2.5/2.5CI-Tests — 2 out of 2 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0.8/2.5Contributors — project has 1 contributing companies or organizations -- score normalized to 3
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 7 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — no data
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — no data
7.5/7.5Token-Permissions — GitHub workflow tokens follow principle of least privilege
6/7.5Vulnerabilities — 2 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4.8
Excluded from scoring (no data or not applicable): packaging, 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.

70Good · 0% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 100 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share1
agent_instruction_files
agent_instruction_max_bytes
How it's scored
18/18One-command bootstrap — Makefile
22/22Automated tests
11/11Lint / format config — .golangci.yml
11/11Static type checking — Go (statically typed)
10/10Reproducible environment — lockfile
10/10Demonstrated agent practice — 47 of the last 100 commits agent-authored or agent-credited
0/8Automated maintenance — no automated dependency updates observed
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfilesgo.sum
has_dockerfileno
typed_languageyes
bootstrap_filesMakefile
has_devcontainerno
has_linter_configyes
typecheck_configs
agent_commit_share0.47
toolchain_manifestsgo.mod
dependency_bot_commit_share0
How it's scored
45/45Type-checkable code — Go (statically typed)
55/55Manageable file sizes — 0/154 source files over 60KB
Inputs used
primary_languageGo
largest_source_bytes52,130
source_files_sampled154
oversized_source_files0

Key facts

0GitHub stars
1contributors
151commits, last 12 months
1days since last push
7releases
1bus factor
3open issues
Gopackage ecosystems

Data collection warnings

  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

More detail

OpenSSF Scorecard 4.8 / 10
4.8aggregate

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-28 02:48 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests2 out of 2 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
3Contributorsproject has 1 contributing companies or organizations -- score normalized to 3
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
n/aPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
n/aSigned-Releasesno releases found
10Token-PermissionsGitHub workflow tokens follow principle of least privilege
8Vulnerabilities2 existing vulnerabilities detected
Direct dependencies 13
RegistryPackageVersion constraintManifest
Gogithub.com/JohannesKaufmann/html-to-markdown/v2v2.5.0go.mod
Gogithub.com/PuerkitoBio/goqueryv1.11.0go.mod
Gogithub.com/alicebob/miniredis/v2v2.38.0go.mod
Gogithub.com/anatolykoptev/go-enginev1.42.0go.mod
Gogithub.com/anatolykoptev/go-imagefyv0.1.1go.mod
Gogithub.com/anatolykoptev/go-kitv0.96.0go.mod
Gogithub.com/anatolykoptev/go-stealthv1.21.0go.mod
Gogithub.com/astappiev/microdatav1.0.2go.mod
Gogithub.com/markusmobius/go-trafilaturav1.12.2go.mod
Gogithub.com/redis/go-redis/v9v9.18.0go.mod
Gogolang.org/x/netv0.57.0go.mod
Gogolang.org/x/syncv0.22.0go.mod
Gogolang.org/x/timev0.15.0go.mod
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": [
        "data-enrichment",
        "go",
        "golang",
        "mcp",
        "mcp-server"
      ],
      "is_fork": false,
      "size_kb": 680,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Go": 1033123,
        "Makefile": 150
      },
      "pushed_at": "2026-07-26T06:09:15Z",
      "created_at": "2026-03-01T00:40:20Z",
      "owner_type": "User",
      "updated_at": "2026-07-26T06:09:21Z",
      "description": null,
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Go",
      "significant_languages": [
        "Go"
      ]
    },
    "owner": {
      "blog": "https://koptev.me",
      "name": "Anatoly Koptev",
      "type": "User",
      "login": "anatolykoptev",
      "company": "oxpulse.chat",
      "location": "San Francisco",
      "followers": 14,
      "avatar_url": "https://avatars.githubusercontent.com/u/239934030?v=4",
      "created_at": "2025-10-24T06:10:43Z",
      "is_verified": null,
      "public_repos": 94,
      "account_age_days": 276
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v1.40.0",
          "kind": "minor",
          "published_at": "2026-07-09T23:49:17Z"
        },
        {
          "tag": "v1.35.0",
          "kind": "minor",
          "published_at": "2026-07-04T02:50:43Z"
        },
        {
          "tag": "v1.34.0",
          "kind": "minor",
          "published_at": "2026-07-04T02:08:59Z"
        },
        {
          "tag": "v1.25.0",
          "kind": "minor",
          "published_at": "2026-07-02T06:31:20Z"
        },
        {
          "tag": "v1.24.1",
          "kind": "patch",
          "published_at": "2026-07-02T05:03:04Z"
        },
        {
          "tag": "v1.24.0",
          "kind": "minor",
          "published_at": "2026-07-02T03:53:30Z"
        },
        {
          "tag": "v0.12.0",
          "kind": "minor",
          "published_at": "2026-03-04T10:34:29Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "41513a37892fe3c0b3ee2f3548aa89e664fedcf8",
          "body": "…oded literal (#64)\n\nReplace the hardcoded Chrome/131 User-Agent in maps/yandex.go fetchOrgStatus\nwith stealth.UserAgentForProfile(stealth.ProfileChrome131). The UA now comes\nfrom go-stealth's BuiltinProfiles (the matched pair for the TLS fingerprint)\ninstead of a hand-written literal that can drift\n[…]\nChrome major as ProfileChrome131 (the invariant the change establishes).\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "refactor: derive Yandex Maps UA from go-stealth identity, not a hardc…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-26T06:09:13Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9841c8998982b9454ca1e4d00fb6e5e1c01c81e2",
          "body": "Bug fixes in v1.19.1:\n- fix(roundtripper): preserve multi-value Set-Cookie headers correctly\n- fix(proxypool): guard Webshare.Next() against empty pool panic\n- fix(oxbrowser): check json.Marshal errors in Solve/FetchSmart/Analyze\n- refactor: consolidate extractDomain into shared internal/uri.ExtractHost\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "deps: bump go-stealth to v1.19.1 (#63)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-18T20:14:42Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8fd81d8f8371fc19bf415dc3fa390a4fd6a0221b",
          "body": "Generated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "chore: scrub private-infra references + drop internal plans (#62)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-18T14:46:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3dc65c70113a3180ceeddeeb0da7da7592d21ab2",
          "body": "…price facts (#56) (#60)\n\nrePrice captured any 2-80 chars after \"цена\"/\"стоимость\"/\"price\", with no\nrequirement that the text contain a digit or price-shaped structure. On real\npages the keyword frequently appears in marketing prose (\"Цена: уборки за 30\nминут гарантированно!\") or schema fragments (\"\n[…]\nolden fixture\neasyweddingday \"от 120 000 ₽\" all still extract correctly.\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(extract): tighten rePrice to reject prose/schema-tier garbage as …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T13:26:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2753e72a471f61c871c6b86aae1afdd74a2e567f",
          "body": "… clean pages (#55) (#58)\n\n* fix(extract): demote social-link phone below labeled contacts tel: on clean pages\n\nA WhatsApp/social-link phone (wa.me / api.whatsapp.com) was ranked as the\nTOP phone authority (tierSocialLink), unconditionally overriding a page's\nown labeled contacts-region tel: (tierCo\n[…]\nic re-addition (placeholder\nproject id), not part of the original verbatim capture. No behavior\nchange.\n\n---------\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(extract): demote social-link phone below labeled contacts tel: on…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T12:59:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a873dbc8bf32f139117a5bd8594b46d105f9ee45",
          "body": "… (#57)\n\nparseOrgPageJSON had cyclomatic complexity 20 (cyclop max 15), blocking\nCI on main since #52/#53. Extract two cohesive field-population blocks\ninto helpers with zero behavior change:\n\n- resolveOrgStatus: the status resolution (outer switch on data.Status\n  + nested switch fallback to parseO\n[…]\nes. Existing parseOrgPage\ntests pass byte-for-byte on the same fixtures.\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "refactor(maps): extract helpers from parseOrgPageJSON to clear cyclop…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T12:31:04Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "04bed0122c625144f50bc56c4a7836a75b230626",
          "body": "Closes 55 vulnerabilities (7 critical, 3 high, 30 medium):\n\n- golang.org/x/net v0.52.0 → v0.57.0 (8 CVEs: HTML parser DoS,\n  HTTP/2 infinite loop, XSS via duplicate attrs, IDNA bypass)\n- golang.org/x/crypto v0.49.0 → v0.54.0 (25 CVEs: SSH auth bypass,\n  FIDO/U2F bypass, key constraint enforcement, c\n[…]\ny SkipMapsCheck) — already fixed in a prior\ncommit, issue was left open.\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "chore(deps): bump golang.org/x/net,crypto,image,text,sys (CVE fix) (#54)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T01:26:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "7b1c26fc8fe098bc3068b0614920827bcf8abe56",
          "body": "…ckoff (#53)\n\nFive stability + refactoring improvements to the Yandex Maps enrichment\npipeline:\n\n1. Hybrid JSON parser (parseOrgPage): primary path now extracts the\n   business data JSON block from rendered HTML and unmarshals it into a\n   typed struct (yandexBusinessData). Regex patterns remain as \n[…]\nng, retry logic, backoff duration, and\n   context cancellation handling.\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "refactor(maps): hybrid JSON parser, confidence scoring, retry with ba…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T00:59:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4e37b31ef7e12a4d97e48f7cc176226536a3cf2d",
          "body": "…ture (#52)\n\nYandex Maps changed its org page JSON format (discovered 2026-07-15).\nAll 5 regex patterns in parseOrgPage matched the wrong fields:\n\n- Name: \"name\":\"...\" matched JSON-LD WebSite schema \"Yandex Maps\"\n  instead of the business. Now: shortTitle → chain.name → legacy \"name\".\n- Phone: \"form\n[…]\nhone/website — making enrichment results unusable\nfor place collections.\n\nGenerated with [Devin](https://devin.ai)\n\nCo-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(maps): update parseOrgPage regexes for new Yandex Maps HTML struc…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-16T00:09:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2a173bf0adf9e03d1545ac20881516890645ab48",
          "body": "fix(maps): geocoder reads meta.code, trims gate, rejects zero coords",
          "is_bot": false,
          "headline": "Merge pull request #51 from anatolykoptev/feat/twogis-geocoder",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-13T22:25:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b6bf0dafe5c0ca93890af34dc8c034cc9fa7c560",
          "body": "Review found the 2GIS geocoder silently degraded to PlaceNotFound on HTTP-200\nquota/auth failures (meta.code 403/429) — over a 1000-place run a 429 would\nsilently drop every remaining place. Now mirrors TwoGISChecker: non-200\nmeta.code → error (Resilient/breaker sees it). Also: trim the address gate\n(whitespace no longer burns a call), reject zero-coord points (0,0 Gulf of\nGuinea), surface read errors, and request address_name to populate OrgData.",
          "is_bot": false,
          "headline": "fix(maps): geocoder reads meta.code, trims gate, rejects zero coords",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-13T22:21:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "096495d8808b18e1f8f670c742ecfbcbb05d5bc7",
          "body": "… the coarse scope (#50)\n\n* fix(extract): read phone role label from the narrowest live node, not the coarse scope\n\ncontactsTextCandidates read a plain-text phone's role/department context\n(phoneRoleLabelText) from the LIVE phoneValueScope() result directly. When\nthe phone label's own block AND its \n[…]\nstyle/menu leak);\ndeterministically RED without the guard, GREEN with. Full extract package\n316 pass under -race; no golden churn.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "fix(extract): read phone role label from the narrowest live node, not…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-10T03:36:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9b7604a7ce2743ae04c73bea6b6da89d5ebcfeb3",
          "body": "…#49)\n\nphoneRoleLabelText's PRECEDING-context scan picked up a site's global\n<nav> menu as a phone's role label on p45.su (a real СПб market,\nmenu-heavy layout) — the closest qualifying preceding sibling was the\nnav (\"— Main Menu —Главная Покупателям…\"), not the page's actual\ncontent. It classified \n[…]\n), and two arm-\nspecific proofs (ul.navbar class token, div[role=navigation]). All\nfour confirmed RED against unmodified code before the fix.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "fix(extract): skip nav/menu boilerplate in phone role-label scanner (…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-10T01:45:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0652d128dcc00e2c7088288af20e523f6363a884",
          "body": "* feat(extract): classify phone role-label general vs departmental\n\nAdds PhoneNumberFact.RoleLabelRaw + .Role (Solution A1, go-enriche side of\nthe phone role-label hardening). Motivating case: p45.su lists a general\nline first, then a mobile tel: under a PRECEDING leasing-desk heading\n(«…по вопросам\n[…]\nmentalTierWinner,\nTestResolver_AddSiteNumbers_RoleGeneralWinsAcrossPages (cross-page resolver\ncase), TestPhoneRole_IsDepartmental.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "feat(extract): classify phone role-label general vs departmental (#48)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-09T23:49:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "91fe7b1bb9006f8f62de380e98e53235a31c0f98",
          "body": "…fallback (#47)\n\n* fix(fetch): recover TLS-cert-mismatch sites via a guarded plain-HTTP fallback\n\nA site serving a mismatched/invalid TLS cert (real case: p45.su presents a\ncert issued for 000h01.westcall.spb.ru) failed the handshake and collapsed\nto a generic StatusUnreachable, indistinguishable fr\n[…]\n\nserver + assertion rework, not a 2-line parallel change, so out of scope per\nreview guidance; a separate follow-up will cover it.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "fix(fetch): recover TLS-cert-mismatch sites via a guarded plain-HTTP …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-09T23:49:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "33cba6dfd4cdb2da90187b0c963d9482d8c0eaa9",
          "body": "…e (#23) (#45)\n\nThe follow-up flagged jsStringLiteralAssignments' `break` on an unterminated\nstring literal as a possible fail-closed miss (aborting the scan before a\nfollowing valid assignment). Analysis shows it is NOT: that break is reachable\nonly when the inner loop scanned to EOF without a clos\n[…]\nid literal survives a trailing\nunterminated one; a sole unterminated literal yields nothing without panic/hang).\n\ngofmt+vet+full suite green.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "docs(extract): document why the unterminated-literal break is EOF-saf…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-08T23:14:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "de4fe587b62263c0ca8efe6a46f3c651243c8add",
          "body": "The ox-browser readability leg was the only fetch leg with no phase timing —\nits server-side render is the slowest single leg in the pipeline, yet\ninvisible in the per-phase histogram. Add PhaseHomepageOxBrowser and emit its\nwall-clock. It runs in PARALLEL with homepage_fetch, so it overlaps rather \n[…]\ning timing is added.\n\nLoad-bearing test (RED-proven): homepage_ox_browser fires when an ox-browser is\nconfigured. gofmt+vet+full suite green.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "feat(metrics): time the ox-browser leg (homepage_ox_browser) (#33) (#44)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-08T23:14:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "01b5aa2a319bc83856aed6d152882482eff891fb",
          "body": "…31) (#43)\n\n#31: cacheKey omitted SkipMapsCheck, so a skip=true result (maps suppressed)\nand a skip=false result (maps merged) collided on the same key. A verify\ncaller (SkipMapsCheck=true) could poison the cache for a wp_enrich caller\n(false), silently serving it a maps-less blob. SkipMapsCheck gen\n[…]\n verifier reads none of those fields).\n\nTestCacheKey updated to v3 + two SkipMapsCheck distinct-key cases. gofmt+vet+\nfull suite (638) green.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "fix(cache): fold SkipMapsCheck into cacheKey + correct its doc (#30/#…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-08T23:14:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a2f2cb2344ce19a52e0c13197b3d819b866c88c0",
          "body": "…guard (#46)\n\nROOT CAUSE: CI pinned golangci-lint v2.10.1, whose bundled staticcheck does NOT\nmodel testing.T.Fatalf as terminal, so it emits false-positive SA5011\n(\"possible nil pointer dereference\") on the standard test pattern\n`n := find(); if n == nil { t.Fatalf(...) }; ... n.Field` across the r\n[…]\nn\nenriche_renderskip_test.go so the four call sites read a guaranteed-non-nil\nfact directly.\n\nUnblocks the open go-enriche PRs (#43/#44/#45).\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@github.com>",
          "is_bot": false,
          "headline": "ci: pin golangci-lint v2.12.2 (fleet standard) + centralize test nil-…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-08T23:12:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1d557ffb26e0cabfe9f4994655633fd9aa95ea4b",
          "body": "…tch (#42)\n\nfetch.NewStealthClient is go-enriche's DIRECT (no-proxy-required) stealth\nclient -- blast-radius row 7 of the fleet SSRF review (highest risk: any\ncaller that omits StealthWithProxy dials straight from this container).\ngo-stealth v1.18.0 is fail-closed by construction, but its stdlib-onl\n[…]\ntops wrapping\nhttputil.ErrSSRFBlocked. Paired with a positive control\n(TestNewStealthClient_WithoutSSRFGuard_AllowsLoopback) proving the\nopt-out genuinely disables the guard rather than being a no-op.",
          "is_bot": false,
          "headline": "fix(fetch): wire go-kit SSRF guard into go-stealth hooks on direct fe…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-08T10:07:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6487bcc10445179b3f0c15255f86f65363c598d1",
          "body": "…ust set (#41)\n\n* fix(extract): read plain-text contacts phones into the SiteNumbers trust set\n\nClose an anti-fabrication FALSE-NEGATIVE in site-phone extraction. Every\nphone finder in extract/ reads only STRUCTURED/LINKED phones — tel: hrefs,\n[itemprop=telephone], og: meta, JSON-LD, branch/schema J\n[…]\nconTextNotHarvested regression test for MEDIUM-1. Full\n./extract/... suite green; go vet + golangci-lint v2 clean; real\nbankrot-v-spb.ru page re-verified (812 contacts/trustworthy, requisites absent).",
          "is_bot": false,
          "headline": "fix(extract): read plain-text contacts phones into the SiteNumbers tr…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-07T21:32:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4f8870b3764bf4a03fc28610e7d6c6e82cc47572",
          "body": "…emoted (#40)\n\n* fix(extract): static official-site toll-free (8-800) is trustworthy, not prefix-demoted\n\nA static 8-800 tel: href on a firm's own official site (e.g.\nspb.tochka-bankrotstva.ru) is a real published contact, but the SiteNumbers\ntrust sidecar tagged it Anchored=false / Source=\"demoted\"\n[…]\nt.\n- NIT2: comment at tierDemoted=0 documenting the zero-value fail-closed\n  dependency (an unset naturalTier must map to iota-zero so numberIsAnchored/\n  dniTrustworthy fail CLOSED). No logic change.",
          "is_bot": false,
          "headline": "fix(extract): static official-site 8-800 is trustworthy, not prefix-d…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-07T20:36:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2528602cba65e64ba03139d2464f32c6b2fbeafb",
          "body": "doFetch's cross-domain-redirect detection discards the already-fetched\nfinal page's body entirely and returns StatusRedirect with an empty body,\nregardless of whether the caller wants that signal or wants the content.\ngo-wp#190 hit this live: mcmedok.ru and excimerclinic.ru both issue\ncross-domain c\n[…]\nsts)\n  issues a redirect to a literal blocked address, driven through the REAL\n  guarded NewFetcher(WithFollowRedirects()) with no WithClient escape hatch\n  -- the guard refuses the second hop's dial.",
          "is_bot": false,
          "headline": "feat(fetch): WithFollowRedirects Fetcher option (#39)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-04T02:50:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1d48badc2d287b37b278d0923df323e5dec14102",
          "body": "Fetcher.doFetch never set a User-Agent header, so every raw fetch sent\nnet/http's default (Go-http-client/1.1). A go-wp consumer (wp_verify,\ngo-wp #190) needs a browser UA on its raw fetch WITHOUT touching the\nclient/Transport the SSRF guard wraps -- NewFetcher's default client\n(httputil.NewSSRFGuar\n[…]\nserAgent_KeepsDefaultSSRFGuard\n(mirrors ssrf_test.go's TestFetcher_RefusesBlockedTargets on the real\nguarded default with WithUserAgent added -- proves the option does not\ndisturb guard construction).",
          "is_bot": false,
          "headline": "feat(fetch): WithUserAgent Fetcher option (#38)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-04T02:08:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "70784551f557db3642080aaee853a4cbb30fb05d",
          "body": "…ncy (#37)\n\nAdd a nil-safe Metrics.OnPhaseTiming(phase, seconds) hook (mirrors the existing\nphoneSource/targetBlocked labeled-callback pattern) plus exported Phase* string\nconsts so a consumer's histogram labels cannot drift from the emitter.\n\nInstruments five phases with time.Since:\n  - total      \n[…]\naller. Test drives Enrich through a stub fetcher\nand asserts homepage_fetch + total fired with non-negative durations (RED-proven\nby neutering the phaseTiming wrapper), plus a nil-hook no-panic guard.",
          "is_bot": false,
          "headline": "feat(enriche): OnPhaseTiming hook — durable per-phase enrichment late…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T11:22:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1ffa2dd065b129cc76bc81bac88d30461b64a1b3",
          "body": "…osure-check (#34)\n\nAdd an additive Item.SkipMapsCheck bool (default false — byte-identical for\nevery existing caller). Honored at the single gate in checkMapsStatus: when\ntrue the ModePlaces maps closure-check (mapsChecker.Check) is skipped exactly\nlike the mapsChecker==nil path — returns \"\" so rec\n[…]\nlled, maps phone never reaches\nfacts under SkipMapsCheck=true; proven RED with the gate clause reverted) plus\nan anti-vacuous control (default-false still calls the checker once and merges\nits phone).",
          "is_bot": false,
          "headline": "feat(enriche): SkipMapsCheck lever to suppress the ModePlaces maps cl…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T09:27:09Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d202589da135db316c3ee42c40408fd02830550e",
          "body": "…-8 ops revert lever\n\nThe v1.30.0 render-skip has a one-way DATA consequence (a false-negative skip auto-writes a number to a live paid card that a code revert can't un-publish), so ops need to disable it WITHOUT a redeploy. WithRenderSkipDisabled(disabled bool) Option + renderSkipDisabled field, th\n[…]\nDR-4 single-definition fitness test still passes. Package stays env-free — go-wp drives it from a container env (WP_RENDER_SKIP). 2 goldens (switch ON → renders again; default → skips), RED-on-revert.",
          "is_bot": false,
          "headline": "feat(enriche): render-skip kill-switch (WithRenderSkipDisabled) — ADR…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T07:31:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "380da4fa04f7dd725c98724be956fab87b90667a",
          "body": "…nchored SiteNumber\n\nEnrich() render-escalation gate consulted only the single-winner Facts (hasContactFacts), never the SiteNumbers SET — so a raw branch_json/schema_place anchored number (mcmedok's real СПб line, already in raw HTML) couldn't suppress a needless 15-30s go-wowa render. Fix: ONE sha\n[…]\n (CHANGES→fixed: RenderSkipped-on-degrade, doc future-tense, contacts SiteNumbers hoist, fitness line-window). Phase-1.5 (go-wp Correctable gate + kill-switch + canary + live probe) is a separate arc.",
          "is_bot": false,
          "headline": "feat(enriche): render-skip when raw fetch already has a trustworthy a…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T07:11:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fd99e689713e107fe4c4141dd1a9c1c9bc9cc75b",
          "body": "…mbers\n\nSibling to the branchJSON finder (v1.28.0): a second additive SiteNumbers finder for national chains that mark up several branches as schema.org LocalBusiness/location entries, each with its own telephone+city. extract/schemaplace.go schemaPlaceCandidates reuses the existing structured.Data.\n[…]\nniTrustworthy/branchjson.go/go.mod/go.sum byte-unchanged. REPRESENTATIVE @graph golden (812 СПб + 495 Moscow) + 0/1/2-place no-op guard + JSON-LD-skip regression. Reviewed by pr-review-council (NITS).",
          "is_bot": false,
          "headline": "feat(extract): schema.org multi-location phone extraction into SiteNu…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T04:04:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6841d3b75f299377abc4874b41b38e9c49276e68",
          "body": "…(national chains)\n\nNational-chain sites (mcmedok.ru, Bitrix) embed real per-branch phones in an inline-script JS string literal (var marker_data='[{phone,phoneLink,region,...}]'); the only tel: href is a Moscow number, so CollectSiteNumbers missed the СПб branch numbers → wp_verify falsely 'wrong'.\n[…]\n 6 goldens incl. the mcmedok REAL capture + anti-fab junk + JSON-LD-skip + DNI-collision. Reviewed by pr-review-council (CHANGES→fixed: dropped kitllm.ExtractJSON prometheus footprint + skip ld+json).",
          "is_bot": false,
          "headline": "feat(extract): branch-locator JSON phone extraction into SiteNumbers …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-03T03:33:46Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6399defe3d790f839f6edc398d7dab0449aedc31",
          "body": "…tyForeign)\n\nP1 of the city-aware phone-verify arc. Adds seed-independent city membership to Result.SiteNumbers so go-wp wp_verify (P2) can pick a CITY-LOCAL correct-to and never feed a wrong-city number onto a paid card. extract/citycode.go: isRUGeographicLandline (delegates to a new isGeographicAr\n[…]\nhully/non-RU no-op). Mobile/8-800/80x-service = neutral (never excludable). Reviewed by pr-review-council (NITS→fixed: reuse isTollFree, single-source numbering, 80x correctness, dedup-survival test).",
          "is_bot": false,
          "headline": "feat(sitenumbers): tag SiteNumbers with city membership (CityMatch/Ci…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-02T21:42:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ef8407bd2da42e584c8dec125c71db5f7d087364",
          "body": "Facts.Phone carry-forward at enriche_fetch.go used rawFacts.PhonePoisoned alone, but resolvePhoneForCityDNI short-circuits on an empty phone-candidate set before its DNI check — so a raw homepage running a DNI widget with ZERO phone candidates never set PhonePoisoned. A self-removing widget could th\n[…]\nts.Phone and the SiteNumbers sidecar. Twin of the SiteNumbers Critical closed in #27; /contacts path already covered via fetchContactsHTML rawPoisoned. Regression test + falsification (RED-on-revert).",
          "is_bot": false,
          "headline": "fix(enriche): close Facts.Phone Poison-OR zero-candidate DNI gap",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-02T12:01:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3a4b9c8f5ffb34869ca8121b4bd90ad173e9574e",
          "body": "…(P1+P2)\n\nP1: factor stripNoise (script/style/noscript/template/svg/head only) and scope applyRegexFallback to it, killing CSS/JS numeric junk (novoclinicspb.ru 84615384615 from an inline letter-spacing rule) WITHOUT stripping footer/.widget legit contacts.\nP2: additive read-only Result.SiteNumbers \n[…]\nt cannot launder a rotating-proxy tel: into Trustworthy=true. Facts.Phone / pickPhoneCandidate byte-stable. Reviewed by pr-review-council (3 rounds); all High/Critical closed with falsification proof.",
          "is_bot": false,
          "headline": "feat(extract): site-number SET sidecar + noise-scoped regex fallback …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-02T11:45:23Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e65c5583a8d810b3f55d3a2d622a08c46f61c368",
          "body": "…(#25)\n\ngo-enriche's fetch/ssrf.go reinvented the connect-time SSRF dial guard\ninstead of consolidating with go-kit's canonical, exported equivalent\n(httputil.NewSSRFGuardedClient / CheckURL / CheckRawURL / GuardedDialContext,\ngo-kit v0.95.0) — flagged Critical by pr-review-council on go-enriche's o\n[…]\ns), golangci-lint 0 issues. Confirmed a real public site still fetches\nend-to-end through the new guarded default (https://example.com/ ->\nStatusActive) — zero false refusal from the migration itself.",
          "is_bot": false,
          "headline": "refactor(fetch): retire local SSRF guard, route onto go-kit httputil …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-02T06:31:17Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f394b4de27304485f9c9c133a0c78beb9356c8ff",
          "body": "…enance (#24)\n\ndropPhone() nils the phone on a DNI/call-tracking verdict but never set\nFacts.PhonePoisoned, and snapshot() only serializes provenance for\npresent (non-nil) fields — so a poison-locked phone came out of Enrich\nindistinguishable from a genuinely absent one on both signals\n(PhonePoisone\n[…]\ngression tests to assert both signals, and\nfalsifies: reverting the one-line PhonePoisoned=true flips\nTestEnrich_ContactsPage_DNIOmitsPhoneKeepsEmailHours and\nTestEnrich_DNISite_MapsPhone_Omitted RED.",
          "is_bot": false,
          "headline": "fix(resolve): propagate DNI poison verdict onto Result.Facts and Prov…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-02T05:03:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9fdebde280b09d3ea7ee26352be5278c0724551f",
          "body": "* chore(lint): extract repeated status/vendor string literals to constants\n\nThe repo's golangci-lint pre-commit hook runs whole-repo (not diff-scoped),\nso these pre-existing goconst violations block ANY commit, including the\nunrelated SSRF-guard fix in the following commit. Pure mechanical refactor,\n[…]\ngoes RED, restored,\n  confirmed GREEN.\n\nVerify: gofmt clean (touched files) / go build ./... ok / go vet ./... ok /\ngo test -race -count=1 ./... ok (all 8 packages) / golangci-lint run ./...\n0 issues.",
          "is_bot": false,
          "headline": "feat(fetch): add SSRF guard for caller-supplied fetch targets (#23)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-07-01T22:11:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "18173b96366b1743a6f8f382f25d9a2f4ac46055",
          "body": "…und failover) (#22)\n\ngo-engine v1.42.0 pulls go-kit v0.91.1: LLM fallback chain advances on\n422/400 model-not-found. Keeps go-enriche on the current auto-rotation kit.",
          "is_bot": false,
          "headline": "chore(deps): bump go-engine to v1.42.0 (go-kit v0.91.1 — model-not-fo…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-26T21:33:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "af4c507f2f404ae2417a7cbe34a17fb5cbfabf33",
          "body": "…ix (#21)\n\nAdopts go-kit #125 ModelRegistry /v1/models auth fix via go-engine@v1.38.0. The health-aware model chain now actually filters dead models instead of degrading on a proxy 401. Proven live on dozor. Mechanical bump.",
          "is_bot": false,
          "headline": "chore(deps): bump go-engine@v1.38.0 for health-aware LLM chain auth f…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-20T03:09:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aa67f701905b936bb578c4bfd3577a4490025cc1",
          "body": "…51 Phase C] (#20)\n\n* feat(enriche): split legal vs venue address — protect the map slot [#51 Phase C]\n\nA venue-geo address and a registered legal/entity address were collapsed into\none Facts.Address. The source-priority resolver (official_site > maps) then let\na high-confidence official-site LEGAL \n[…]\nsertions (legal seat → LegalAddress, venue → map\n  slot) are unchanged.\n\nNet production-code delta: -29 LOC (deletion-dominant). go build/vet/test/-race\nall green; golangci-lint v2.10.1 (CI) 0 issues.",
          "is_bot": false,
          "headline": "feat(enriche): split legal vs venue address — protect the map slot [#…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-18T10:35:07Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1577c124fd05dcec32f4093b1f965e161b5d7eb1",
          "body": "…mail/hours/address) [#51 Phase B] (#19)\n\n* feat(enriche): discover the contacts subpage + extract email/hours/address (multi-field)\n\nThe homepage often carries NO contacts but links a dedicated /contacts page\nthat holds the canonical, richer contact set (email, working hours, address)\nthe homepage \n[…]\nte + provenance,\nlonger-address-wins; + slug-table cases. go test -race ./... green;\ngolangci-lint v2.10.1 0 issues.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(enriche): contacts-subpage discovery + multi-field extraction (e…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-18T05:19:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "da138bc75579a56c6bba7db1fbfec91705dd4f34",
          "body": "…absent (#18)\n\nThe headless-browser render (WithBrowserFetch) previously fired ONLY when\nreadability content was thin (< minExtractChars). On a text-rich SPA venue\nsite whose contacts/hours block is injected by JS (Tilda/Bitrix/React), the\nraw HTTP fetch sees the article text but NO structured conta\n[…]\nno over-render when a raw contact fact is present; thin_content reason\npreserved; render that adds nothing keeps the raw facts.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(enriche): render JS-heavy official sites when contact facts are …",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-18T02:09:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "03aa5bea706f6288e2b7e3acfe14c08dadd80051",
          "body": "…rch (#17)\n\nThe DNI omit landed only at the extract layer: ExtractFactsForCity set\nfacts.Phone=nil on a DNI-no-social page, but a nil phone is indistinguishable\nfrom \"the site had no phone\". The source-priority resolver merges the maps card\nphone at sourceMaps BEFORE the site fetch, then mergeSite c\n[…]\n green -race;\ngolangci-lint v2.10.1 (CI version) 0 issues.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>\nCo-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(enriche): DNI-poison is a resolver verdict that outranks maps/sea…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-18T00:37:54Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f27a6c686eede91b03ad36371d6a0358e15cd2ca",
          "body": "…tating proxy, never ship it (#16)\n\nPhase-3 PR-4: close the DNI-no-social wrong-phone vector. The Phase-1 social-link\nrank saved Royal Wedding (it has a wa.me link), but a venue running a\ndynamic-number-insertion vendor with NO hard-coded social link still shipped its\nrotating contacts-region tel: a\n[…]\n2) tel: (Игора-Драйв class — the discriminator)\n- golden regression set unchanged (igora 615-70-00, royal-wed social 921, etc.)\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(extract): DNI/call-tracking vendor-signature detection — omit ro…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-17T22:16:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "62fd5d8c62f71cc0e09c1621332e0792599a3b8d",
          "body": "…r + cache v2 (#15)\n\n* feat(enriche): persisted per-field provenance + operator_verified tier + cache v2\n\nPhase 3 (ONE_WAY contract) of official-site-first. Promotes the in-call\nsource-priority provenance to the public Result so the content layer can\nPERSIST {source, confidence} per fact and protect\n[…]\nst\n(reintroduced in the DNI sub-phase that emits it).\n\ngo test -race -count=1 green; golangci-lint v2.10.1 0 issues.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(enriche): persisted per-field provenance + operator_verified tie…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-17T20:04:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7c88798bfc662ae9b8eb1237adee68808cb361f4",
          "body": "… conflict (#14)\n\n* feat(enriche): source-priority resolver — official site beats maps on conflict (#14)\n\nPhase 2 of official-site-first enrich. Collapses the two independent fact\nwriters in enriche.Enrich (the maps OrgData merge + the wholesale site-extraction\nassign) into ONE authority — a source-\n[…]\nnt now describes\nthe order-independent override+rejection semantics; errgroup moved to the\nthird-party import group.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(enriche): source-priority resolver — official site beats maps on…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-17T18:23:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "25615e666f9143f1f0d548b81dcbd7769f9bdc04",
          "body": "…l: is top phone authority (#13)\n\n* feat(extract): tel/mailto/microdata contact extraction, contacts-region tel is top phone authority\n\nThe wrong-phone defect class (Royal Wedding, Igora-Drive) had a single root\nmechanism: ExtractFacts never read the venue's own tel: href. Its phone cascade\nwas JSON\n[…]\n> 0 issues (config verify clean). Tests\n1.24/1.25 already green; go build/vet/test ./... and -race ./extract/ green.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(extract): tel:/microdata contact extraction — contacts-region te…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-17T17:26:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "706672fad13e05db953dcae05c9daa66d2a61fba",
          "body": "…e (#12)\n\ncheckMapsStatus mapped only PlacePermanentClosed; PlaceTemporaryClosed\nfell through to fetchAndExtract which set StatusActive on a live website\n(F14: closed venues shown as active). Add StatusTemporaryClosed and\nshort-circuit on temporary closure. Error path stays fail-open by design;\nobservability lands separately in go-wp via OnMapsCheckError.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(maps): map temporarily-closed venues instead of dropping to activ…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-03T22:35:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aa0443470cb3cfc9dc96d5ddb38aa11adc824bce",
          "body": "NewDaData resolves a RU address to coords via DaData clean/address, gating on\nqc_geo ∈ {0,1} (house-level) so only accurate coords pass; street/city\nfallbacks → PlaceNotFound. Address- and credential-gated: empty address or\nmissing key → PlaceNotFound (inert), so it can be wired before the key is\nprovisioned. Bad-key/quota HTTP errors surface as errors, not silent\nnot-found. ToS permits coord storage (unlike Yandex).\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(maps): DaData address geocoder (FIAS/ГАР), house-level gated (#11)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T06:53:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db9dfdbbafe9922d7f192dabab5725d0f7d9ab61",
          "body": "…oids (#10)\n\n2GIS /items/geocode returns meta.code=200 with a street/adm_div centroid when\nit can't resolve the exact building — a confident-wrong coord. Gate on\nitems.type: accept only building-level hits, reject coarse fallbacks →\nPlaceNotFound so the composite falls through to the anchored by-name checker.\nThis is what let the geocoder regress enrichment when wired first.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(maps): geocoder accepts building-level only, rejects coarse centr…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T06:28:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f255a17a87695f2ad92a5cc13705e13346641d54",
          "body": "NewTwoGISGeocoder resolves a street address to building-level coords via\n2GIS /items/geocode, fixing the wrong-venue class the by-name /items search\nproduces (~53% of SPb places). Address-gated: empty address → NotFound so a\ncomposite falls through to the by-name checker. Reuses TwoGISConfig +\ntwoGISResponse/Point types.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(maps): 2GIS geocoder (address→coords) Checker (#9)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T05:52:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c92deddff0898a6e63e4fb0be001a5b548190405",
          "body": "* feat(maps): add address-anchored lookup to fix 2GIS name mis-resolution\n\nLatin↔Cyrillic transliteration causes 2GIS to return fuzzy/nearby results\nwhen a place name does not match its indexed form (e.g. \"Singer\" resolves to\n\"Giovanni medici\" at a totally different street). Anchoring on the discove\n[…]\nntical after adjective stripping. Falls through to Yandex rather than\nreturning wrong coordinates — safer direction.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(maps): address-anchored lookup to fix 2GIS name mis-resolution (#8)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T01:16:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b362d93d16e6f75ae257ec6cef73736e37815d5d",
          "body": "The upstream golangci-lint pre-commit hook runs without GOWORK=off; on krolik\nthe ~/go.work references missing modules, so it fails in git worktrees and\nforces --no-verify. Mirror go-engine/go-wp: a local system hook that runs\nenv GOWORK=off golangci-lint run.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "chore(ci): run golangci-lint via local hook with GOWORK=off (#7)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T00:34:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bf0c16d5445d951c5db2b3992812cebac9e6e2e5",
          "body": "…kup) (#6)\n\n* feat(item): source-provided coords seeding with precedence over maps lookup\n\nDiscovery sources like KudaGo return authoritative coordinates per event/place.\nWithout this, the maps checker (2GIS name search) could mis-match — e.g. resolve\n\"Кафе Singer\" to a different venue entirely — ov\n[…]\nchecker, coords must survive\n- TestEnrich_SourceCoords_PairGuard_LonOnly: lone Longitude (nil Lat) treated as absent\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(item): source-provided coords seeding (source wins over maps loo…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-02T00:10:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d91382ff462e884f22ef379b240fa40d4ad165e5",
          "body": "* feat(maps): populate coords from 2GIS + resilient circuit-breaker decorator\n\nTask 1 - 2GIS coords (twogis.go):\n- Add items.point to the fields query param so 2GIS returns coordinates.\n- Add twoGISPoint struct (lat/lon) and Point field on twoGISItem.\n- Populate OrgData.Latitude/Longitude from the p\n[…]\n updated to assert (nil, err) / ErrSuspended shapes; new test\n  TestResilient_ParentCancelNotChargedToBreaker added.\n\n---------\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "feat(maps): 2GIS coords + resilient circuit-breaker decorator (#5)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-01T23:07:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ae78ccd800104c34b38e5255c823a5c0620815db",
          "body": "Forward-align with the web-primitive restructure tail. go-enriche imports only\ngo-engine/websearch (no webcache/webtext) so it was immune to the go-stealth\nv1.15.0 removals, but should not stay on stale pins. Pure dep bump; build + all\ntests green.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "chore(deps): bump go-engine v1.27.0 + go-stealth v1.15.0 (#4)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-01T22:10:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "63359500e941e6627f71926f6c5e67cecd76c856",
          "body": "go-stealth v1.13.0 removed the websearch package (moved to go-engine, go-stealth\ncommit 178810a). Swap the 5 importers (search/ddg.go, search/oxbrowser.go,\nsearch/startpage.go, search/search.go, maps/search_oxbrowser.go) from\ngo-stealth/websearch to go-engine/websearch — the API is identical\n(ParseD\n[…]\ns the last cross-repo importer of the deleted\ngo-stealth/websearch, unblocking go-wp to drop its replace => go-stealth v1.12.0.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "refactor: migrate websearch import go-stealth -> go-engine (#3)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-01T20:20:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cff870b067f04612d6f381cd57813703fb39fa2d",
          "body": "Yandex Maps org pages render to ~650KB; the 512KB LimitReader cap\ntruncated them mid-JSON, so org-data parse failed and lat/lon/rating/hours\ncame back empty. Validated on 5 SPb venues: coords 0/5 -> 3/5, accurate.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>",
          "is_bot": false,
          "headline": "fix(maps): bump oxFetch maxResponseBytes 512KB->4MB (#2)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-06-01T19:31:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b9e5ca731d1e1ea14034297096b7cef925d25eb6",
          "body": "Documents Phase 4 search-consolidation decision (go-enriche becomes\nthe canonical search engine) and the counter-task waiting in go-search.\n\nCo-authored-by: Anatoly Koptev <anatolykoptev@users.noreply.github.com>\nCo-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add Content Automation Track to ROADMAP (#1)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-04-25T06:29:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5175dd010299632233e70013cc73fb3255705d4d",
          "body": "…_web_search",
          "is_bot": false,
          "headline": "refactor: migrate go-search client from /api/search to /api/tools/raw…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-04-16T05:52:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9e42bc8607093c937fc7f997c9ca2a53257a1184",
          "body": null,
          "is_bot": false,
          "headline": "chore(deps): bump go-stealth to v1.12.0",
          "author_name": "Anatoly Koptev",
          "author_login": null,
          "committed_at": "2026-04-11T22:46:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5710b625a9ee1adbf18afa89861c5f2a37c8c575",
          "body": "GoBrowserSearch + GoBrowserOrgFetcher replace ByparrSearch + ByparrOrgFetcher.\nUses go-browser headless Chrome /render endpoint instead of FlareSolverr /v1.\nExtracted extractOrgURLsFromHTML as a reusable helper.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: replace Byparr Yandex Maps search with go-browser /render",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-28T04:26:09Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9d0fffee51e55265d6981aaa0cdd5b1c507775f6",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add OxBrowserOrgFetcher - fast Chrome TLS org page fetch",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T04:02:07Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "07930e756dcd3622c8eaaebc2904a2b3df05a326",
          "body": "Yandex Maps org pages are SPA — plain HTTP returns empty skeleton.\nByparrOrgFetcher uses headless browser to render full HTML with status.\nThis is required for maps.Checker to actually detect closed places.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add ByparrOrgFetcher for rendering Yandex Maps org pages",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T03:37:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ddfff4538877c98aa41c5c7ba1a192c3c4b65571",
          "body": "- Extract oxfetch.go: shared ox-browser /fetch helper (eliminates duplication)\n- Rename oxMaxResponseBytes → maxResponseBytes (package-wide constant)\n- Use bytes.NewReader instead of strings.NewReader(string(body))\n- Remove hardcoded saint-petersburg from URLs (city in query)\n- Simplify search_oxbrowser.go and search_yandex_maps.go via oxFetch\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor(maps): extract shared oxFetch helper, fix code review issues",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T03:12:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "af74282b336aece60e079894efaeca944353d9ff",
          "body": "Yandex Search API finds org URLs natively (~2s vs ~7s with all engines).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): GoSearchSearch uses engine=yandex for faster org URL lookup",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T02:56:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "46ef928b3035cb60f1c0c3834dd9e04e1ea17a75",
          "body": "Calls go-search /api/search endpoint which aggregates DDG + Startpage +\nBrave in parallel. Replaces SearXNG dependency for maps.Checker.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add GoSearchSearch - REST API search via go-search",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T02:37:43Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c30984192c2079f3ceafabbb305143b08a2514f1",
          "body": "Uses FlareSolverr-compatible headless browser to render Yandex Maps SPA\nand extract org URLs from solution.url redirect + rendered HTML.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add ByparrSearch - headless Yandex Maps search via byparr",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T02:14:27Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6f5fbbec20aae9c81f4a1a8880bf314c9f0fed5d",
          "body": "…rowser\n\nQueries yandex.ru/maps/{city}/search/{name}/ to find org URLs.\nMuch higher hit rate than DDG site: queries. No API key needed.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add YandexMapsSearch - direct Yandex Maps search via ox-b…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T02:03:38Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "273669dc43f279301ac5d9e3b81ba0da482d4712",
          "body": "YandexMaps checker now supports WithSearchFunc option for web search,\nremoving hard dependency on SearXNG. Includes OxBrowserSearch adapter\nthat uses DDG HTML via ox-browser /fetch endpoint.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add pluggable SearchFunc as SearXNG replacement",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-24T01:51:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "20cb1958173a7a019dc311806813e538432de377",
          "body": "Captures og:image from ox-browser /read response for use in research pipelines.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(fetch): add OGImage field to ReadabilityResult",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-23T22:46:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "171ff6c2124ab3ce60d3750c5db84874574ed25f",
          "body": "Passes JSON-LD blocks from ox-browser /read response to consumers.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(fetch): add JsonLD field to ReadabilityResult",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-23T03:06:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "df683f8c60cded627ae764098916711b869a6182",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: expose Search() method on Enricher for direct queries",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-23T01:36:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "adf702006d88fdfcdf34fb848c4a95145fc06175",
          "body": "…etadata\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(search): add SearchEntry to SearchResult for individual result m…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-23T01:34:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3ee847ab5d55414e445ca4209d282b52eb323fd6",
          "body": "The unified /read endpoint provides the same content extraction\nplus CF bypass, markdown output, and anti-bot detection.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: switch from ox-browser /readability to /read endpoint",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-23T01:19:50Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5af5c3a9fcf73c4dd3eb11abda5c1878b3bd2ad4",
          "body": "…alidateAddress/ValidatePrice)",
          "is_bot": false,
          "headline": "chore(extract): remove unused old snippet validators (superseded by V…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:39:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d0335ed83a13e91873003fa515a515cdca8ebaf3",
          "body": "…e validators\n\nWire ValidatePhone, ValidateAddress, ValidatePrice into ExtractSnippetFacts,\nreplacing raw regex validators. Phone validation was entirely missing from\nsnippet extraction — garbage numbers like 81063196745 are now rejected.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): add phone validation to snippets, upgrade address/pric…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:37:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "dbb044adc6f6ca0a9162b890645378bece803cb4",
          "body": "… + regex)\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): wire validators into ExtractFacts pipeline (structured…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:33:35Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0b1d199019eec403784545f409da0360cfe03989",
          "body": "…ing text\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): add address validator filtering page titles and market…",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:30:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "216585da9b87de1963df1577e1f5132baf07a3a4",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): add price validator filtering CSS/HTML/JS garbage",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:28:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1287c0023c0d0a2d619076198cb648a615ccf108",
          "body": "ValidatePhone validates Russian phone numbers by checking 3-digit\narea/operator codes against Rossvyaz ranges (mobile DEF 900-999,\nlandline 301-499, SPb 800-816, northern 820-879).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): add phone number validator with area code checking",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-21T02:26:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2a766f12195864b60ab9638c28462d80ac0e1cd1",
          "body": null,
          "is_bot": false,
          "headline": "chore: sync go.sum",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-09T23:50:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8386ce78a694ca8ac066c62622a4ad762d714310",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: add GOWORK=off to build tooling",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-09T23:37:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "be6357f8ec3c225b6b99cda877f1af3c4921716d",
          "body": "- Fix ox-browser /fetch response parsing: Content→Body, StatusCode→Status\n  to match actual ox-browser response format (same bug fixed in go-search)\n- Add SiteHint provider wrapper that appends site: restrictions to queries\n  for prioritizing results from trusted regional sources\n- Update tests to match new response format\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: ox-browser response format + add SiteHint provider",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T19:59:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5e507c372cf51027c1a4d4fa442ce6db0f858c02",
          "body": "- Parallel runs all providers concurrently, merges results from all\n  successful ones (unlike Fallback which returns first success)\n- Move defaultMaxResults from deprecated searxng.go to search.go\n- Increase from 3 to 8 for better fetchSearchSources coverage\n- Fix searxng test to match new default\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(search): add Parallel provider, increase maxResults to 8",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T06:37:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "70b64d6b701ce1299bcd6775332f44da6b12987f",
          "body": "Fetch DuckDuckGo HTML through ox-browser's /fetch endpoint (Cloudflare\nbypass) and parse results with websearch.ParseDDGHTML. Use as a fallback\nwhen direct DDG/Startpage scrapers fail.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(search): add OxBrowser provider",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T06:34:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "412f9c6414332c708e403331d182bc260769bb04",
          "body": "When fetching search source URLs, ox-browser readability now runs in\nparallel with go-stealth HTTP fetch. The longer content wins, improving\nextraction from sites that block direct HTTP requests.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: use ox-browser readability as fallback in search source fetch",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T05:28:15Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fa0c8795c3098520216a97267b830ac8a69d2828",
          "body": "WithOxBrowser(url) option runs ox-browser /readability in parallel with\ntrafilatura. Picks the longer result. Falls back gracefully if ox-browser\nis unavailable. 3 unit tests for the HTTP client.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add ox-browser readability as parallel content extractor",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T05:08:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2ecf68c90ce08d78dcc39a1d04d915e9e955e60c",
          "body": "All search source URLs are now fetched concurrently instead of\nsequentially. Results are assembled in original order after all\ngoroutines complete, keeping content deterministic.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "perf: parallelize search source fetching with errgroup",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T04:33:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a6a1d38bdb8ff5b663c94a6cfea78084b5f2ad3b",
          "body": "When an item has no URL (e.g. nature objects like lakes, rocks), the\nenricher now fetches top N URLs from search results, extracts text via\ntrafilatura, and merges structured facts. This provides content and\nfacts for items that previously only got snippet-level data.\n\nNew option: WithSearchFetchLimit(n) to control max URLs (default 5).\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: fetch search source URLs when item has no primary URL",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-07T04:22:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "eb9cabf7555c12f18539bb8c0d923037462881e8",
          "body": null,
          "is_bot": false,
          "headline": "dep: upgrade go-stealth (ox-browser integration, CF 200 detection)",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-06T20:01:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "16a5840ad054bcaa1e0f270665bdad69b9dcb330",
          "body": "Clean up module references after go-stealth proper tagging.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "deps: use go-stealth v1.5.0, remove replace directive",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-06T07:42:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "19270f8118bf79045a224cc3f0ced2f17170f097",
          "body": "Replace local parseDDGHTML and parseStartpageHTML with websearch.ParseDDGHTML\nand websearch.ParseStartpageHTML from go-stealth. Eliminates duplicated HTML\nparsing logic while keeping the enriche-specific HTTP flow and public API\nunchanged. Cache package kept as-is (different interface design).\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: delegate search parsing to go-stealth v2 websearch",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-06T07:28:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1e8e932e51bd25a8b6e9ef5d33a380080fe56e6f",
          "body": "When SearXNG returns multiple Yandex Maps org URLs for the same\nplace name (e.g. multiple branches), Check() now iterates all results\nand prefers open/temporary-closed branches over permanently closed\nones. Previously it short-circuited on the first parseable result,\nwhich caused false 'permanently_closed' status when a closed branch\nranked above the active one in search results.\n\nAdds TestYandexMaps_SkipsClosedBranch and TestYandexMaps_AllBranchesClosed.",
          "is_bot": false,
          "headline": "maps: prefer open branches over permanently closed ones",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-05T21:16:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "414e3fd3881408823e6801a233b0b4e7df3aa646",
          "body": "Add OrgData struct and OrgFetcher func type for browser-based rendering\nof Yandex Maps SPA pages. When OrgFetcher is set, parseOrgPage extracts\nphone, address, hours, rating, coordinates, website, and categories\nfrom rendered HTML JSON blobs. Data merges into enrichment Facts using\nfill-nil-only pattern, providing fallback for missing Schema.org data.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): extract business data from Yandex Maps org pages",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T05:02:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "44cbb4d7b90049b28e455ad13ec6ac65de6ba0d3",
          "body": "WithRetryOn403, IsProxyRetryable, shouldRetryFetch, retryFetch removed.\nfetchWithRetry reverted to simple transient-only retry. Block retry (403/429)\nis now handled by go-stealth WithRetryOnBlock at the transport level.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: remove 403 retry logic, moved to go-stealth transport layer",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T04:19:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f46cb5c52582c63c669a9c8df5c5548271f2e0a1",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add WithRetryOn403 option — retry 403 with proxy rotation",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T03:53:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "066e101da3e9e905a5f2fbd805a65ffafb1ab1f4",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(fetch): add IsProxyRetryable() for 403 + transient errors",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T03:52:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4057227efb2d90ceca96875f657fb44c2550cb20",
          "body": "Add geocoder field to Enricher, WithGeocoder option, and\ngeocodeIfNeeded method that resolves address to lat/lon coordinates\nafter fact extraction for place items.\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: integrate geocoding into Enricher pipeline for ModePlaces",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T02:50:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3ec7137e04d952ed99b5d6ee869fc203987a1809",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(maps): add Geocoder — SearXNG-based address→coordinates",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T02:48:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "867f215d2499d6bf0915a4880b73303b1ca2ca73",
          "body": "Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(extract): add Latitude/Longitude to Facts struct",
          "author_name": "Anatoly Koptev",
          "author_login": "anatolykoptev",
          "committed_at": "2026-03-04T02:46:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        }
      ],
      "releases_count": 7,
      "commits_last_year": 151,
      "latest_release_at": "2026-07-09T23:49:17Z",
      "latest_release_tag": "v1.40.0",
      "releases_from_tags": false,
      "days_since_last_push": 1,
      "active_weeks_last_year": 15,
      "days_since_latest_release": 18,
      "mean_days_between_releases": 21.3
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": false,
      "has_contributing": false,
      "health_percentage": 28,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/anatolykoptev/go-enriche",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/anatolykoptev/go-enriche",
          "is_deprecated": false,
          "latest_version": "v1.40.3",
          "repository_url": "https://github.com/anatolykoptev/go-enriche",
          "versions_count": 72,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-07-18T20:14:42Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 9
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 0,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_stars": 0,
        "collected_at": null
      },
      "open_issues_and_prs": 3
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 52130,
      "source_files_sampled": 154,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "go.mod"
      ],
      "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": [
        "go"
      ],
      "dependencies": [
        {
          "name": "github.com/JohannesKaufmann/html-to-markdown/v2",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.5.0"
        },
        {
          "name": "github.com/PuerkitoBio/goquery",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.11.0"
        },
        {
          "name": "github.com/alicebob/miniredis/v2",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v2.38.0"
        },
        {
          "name": "github.com/anatolykoptev/go-engine",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.42.0"
        },
        {
          "name": "github.com/anatolykoptev/go-imagefy",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.1.1"
        },
        {
          "name": "github.com/anatolykoptev/go-kit",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.96.0"
        },
        {
          "name": "github.com/anatolykoptev/go-stealth",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.21.0"
        },
        {
          "name": "github.com/astappiev/microdata",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.0.2"
        },
        {
          "name": "github.com/markusmobius/go-trafilatura",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.12.2"
        },
        {
          "name": "github.com/redis/go-redis/v9",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v9.18.0"
        },
        {
          "name": "golang.org/x/net",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.57.0"
        },
        {
          "name": "golang.org/x/sync",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.22.0"
        },
        {
          "name": "golang.org/x/time",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.15.0"
        }
      ],
      "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": 57,
        "open_issues": 3,
        "closed_ratio": 0.571,
        "closed_issues": 4,
        "closed_unmerged_prs": 1
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "anatolykoptev",
          "commits": 150,
          "avatar_url": "https://avatars.githubusercontent.com/u/239934030?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        ".golangci.yml"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": true
    },
    "security_signals": {
      "lockfiles": [
        "go.sum"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 10,
            "reason": "2 out of 2 merged PRs checked by a CI test -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 3,
            "reason": "project has 1 contributing companies or organizations -- score normalized to 3",
            "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 7 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 10,
            "reason": "GitHub workflow tokens follow principle of least privilege",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 8,
            "reason": "2 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "41513a37892fe3c0b3ee2f3548aa89e664fedcf8",
        "ran_at": "2026-07-28T02:48:41Z",
        "aggregate_score": 4.8,
        "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-26T06:10:48Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-07-26T06:09:14Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": [
        {
          "number": 35,
          "created_at": "2026-07-03T10:26:02Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 59,
          "created_at": "2026-07-16T13:23:59Z",
          "last_comment_at": null,
          "last_comment_author": null
        },
        {
          "number": 65,
          "created_at": "2026-07-27T23:12:08Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ]
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/anatolykoptev/go-enriche",
    "host": "github.com",
    "name": "go-enriche",
    "owner": "anatolykoptev"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 59,
      "inputs": {
        "security": 48,
        "vitality": 84,
        "community": 24,
        "governance": 50,
        "engineering": 82
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 84,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 74,
            "inputs": {
              "commits_last_year": 151,
              "human_commit_share": 1,
              "days_since_last_push": 1,
              "active_weeks_last_year": 15
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 1 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 1
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "15/52 weeks with commits",
                "points": 10.4,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 15
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "151 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 151
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 7,
              "latest_release_tag": "v1.40.0",
              "releases_from_tags": false,
              "days_since_latest_release": 18,
              "mean_days_between_releases": 21.3
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "7 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 18 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 18
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~21.3 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 21.3
                    }
                  }
                ],
                "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": 24,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 0,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "0 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 50,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 13,
            "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 1 contributing companies or organizations -- score normalized to 3",
                "points": 3,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "moderate",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 64,
            "inputs": {
              "merged_prs": 57,
              "open_issues": 3,
              "closed_issues": 4,
              "issue_closed_ratio": 0.571,
              "closed_unmerged_prs": 1
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "57% of issues closed",
                "points": 26.7,
                "status": "partial",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 57
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "57/58 decided PRs merged",
                "points": 37.6,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 57,
                      "decided": 58
                    }
                  }
                ],
                "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": 41,
            "inputs": {
              "followers": 14,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "anatolykoptev",
              "public_repos": 94,
              "account_age_days": 276
            },
            "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": "14 followers of anatolykoptev",
                "points": 8.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 14,
                      "login": "anatolykoptev"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "94 public repos, account ~0 yr old",
                "points": 14.5,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 94
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 0
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "github.com/anatolykoptev/go-enriche"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 9
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on go",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "go"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 9 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 9
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "72 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 72
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 82,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 94,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": true
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "1 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": ".golangci.yml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 9.6,
                "status": "met",
                "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": "2 out of 2 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 65,
            "inputs": {
              "topics": [
                "data-enrichment",
                "go",
                "golang",
                "mcp",
                "mcp-server"
              ],
              "has_wiki": false,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": false
            },
            "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": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "5 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "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": 48,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 48,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 4.8
            },
            "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": "2 out of 2 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 1 contributing companies or organizations -- score normalized to 3",
                "points": 0.8,
                "status": "partial",
                "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 7 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "GitHub workflow tokens follow principle of least privilege",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "2 existing vulnerabilities detected",
                "points": 6,
                "status": "partial",
                "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": 70,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 1,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "100 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 100,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0.47,
              "toolchain_manifests": [
                "go.mod"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".golangci.yml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".golangci.yml"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Go (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "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": "47 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 47,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "primary_language": "Go",
              "largest_source_bytes": 52130,
              "source_files_sampled": 154,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Go (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/154 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 154,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-28T02:48:46.947163Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/a/anatolykoptev/go-enriche.svg",
  "full_name": "anatolykoptev/go-enriche",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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