Öffentliches Register
Software-GesundheitsberichtSchema 0.27.0 · Metriken 1.13.0 · 2026-07-28 02:48 UTC

anatolykoptev / go-enriche

GoMIT★ 0 Sterne⑂ 0 Forksseit März 2026Auf GitHub ansehen ↗

anatolykoptev/go-enriche erreicht einen Gesundheitsindex von 59 von 100 und liegt damit im Bereich Mittel. Am stärksten schneidet es bei Vitality (84/100) ab, am schwächsten bei Community & Adoption (24/100). Zuletzt vor 1 Tag aktualisiert. Ein einzelner Mitwirkender trägt den Großteil der jüngsten Arbeit.

59
gesamt / 100
Mittel

Software-Gesundheitsindex

Metriken werden auf einer Skala von 1–100 in gewichtete Kategorien gruppiert. Der Gesamtwert beginnt als ihr Mittel; sobald öffentliche Evidenz die Richtlinie für Hochrisikojurisdiktionen auslöst, wird die Bewertung angepasst und erhält die Obergrenze 49 (Gefährdet). AI Readiness liegt außerhalb.

59
Exzellent85-100Vorbildlich; erfüllt im Wesentlichen alle geprüften Kriterien
Gut70-84Gesund; geringfügige Lücken
Mittel50-69Akzeptabel mit deutlichen Lücken; Überprüfung empfohlen
Gefährdet30-49Erhebliche Schwächen; eine Übernahme erfordert Vorsicht
Kritisch1-29Schwerwiegende Probleme (aufgegeben, nur ein Maintainer, keine Hygiene)
VitalitätCommunity &VerbreitungNachhaltigkeit &GovernanceEngineering-QualitätSicherheitAI Readiness

Bewertungsprofil

Jede Achse ist eine Kategorie. Die Form zählt mehr als der Durchschnitt — ein gesundes Projekt füllt die gesamte Fläche, während ein Profil aus Spitzen und Kratern bedeutet, dass Stärke in einer Dimension Risiken in einer anderen verdeckt.

Eigentümerschaft

Anatoly KoptevPersönliches Konto
14 Follower94 öffentliche Reposseit Okt. 2025oxpulse.chat

Dieses Repository gehört einem persönlichen Konto. Ein Projekt mit nur einem Eigentümer trägt ein höheres Kontinuitätsrisiko als ein organisationsgetragenes.

Paket-Ökosysteme

RegistryPaketVersionDownloads / MonatVersionenZuletzt veröffentlicht
Gogithub.com/anatolykoptev/go-enrichev1.40.3-72vor 9 Tagen

Metriken nach Kategorie

Vitalität

Lebt das Projekt — wird Code geschrieben und werden Releases ausgeliefert?

84Gut · 22 % des Gesamtindex
Wie die Bewertung erfolgt
36/36Push-Aktualität — letzter Push vor 1 Tagen
10.4/36Commit-Rhythmus — 15/52 Wochen mit Commits
18/18Commit-Volumen — 151 Commits im letzten Jahr
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
Verwendete Eingangsdaten
commits_last_year151
human_commit_share1
days_since_last_push1
active_weeks_last_year15

Release-Disziplin

100Exzellent
Wie die Bewertung erfolgt
27/27Liefert Releases aus — 7 Releases veröffentlicht
36/36Release-Aktualität — letztes Release vor 18 Tagen
27/27Release-Rhythmus — ein Release etwa alle 21,3 Tage
0/10OpenSSF Scorecard: Signed-Releases — keine Daten
Verwendete Eingangsdaten
releases_count7
latest_release_tagv1.40.0
releases_from_tagsnein
days_since_latest_release18
mean_days_between_releases21,3
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): OpenSSF Scorecard: Signed-Releases. Die verbleibenden Gewichte wurden renormalisiert.

Community & Verbreitung

Hat das Projekt Nutzer, Downloads, Aufmerksamkeit und ein einladendes Umfeld für Beitragende?

24Kritisch · 18 % des Gesamtindex
Wie die Bewertung erfolgt
0/60Stars — 0 Stars
0/25Forks — 0 Forks
0/15Watcher — 0 Watcher
Verwendete Eingangsdaten
forks0
stars0
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Wie die Bewertung erfolgt
22.5/22.5README
22.5/22.5Lizenz — anerkannte Lizenz (MIT)
0/18CONTRIBUTING-Leitfaden
0/13.5Verhaltenskodex
0/7.2Issue-Vorlage
0/6.3PR-Vorlage
Verwendete Eingangsdaten
has_readmeja
has_licenseja
has_contributingnein
has_issue_templatenein
has_code_of_conductnein
has_pull_request_templatenein

Nachhaltigkeit & Governance

Überdauert das Projekt die Menschen, die es tragen — Bus-Faktor, Reaktionsfähigkeit, Trägerschaft und Paketpflege?

50Mittel · 24 % des Gesamtindex
Wie die Bewertung erfolgt
9/54Bus-Faktor — 1 Beitragende decken die Hälfte aller Commits ab
0/22.5Commit-Verteilung — wichtigste beitragende Person verfasste 100 % der Commits
1.4/13.5Breite der Beitragenden — 1 Beitragende
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
Verwendete Eingangsdaten
bus_factor1
contributors_sampled1
top_contributor_share1
Wie die Bewertung erfolgt
26.7/46.8Issue-Lösungsquote — 57 % der Issues geschlossen
37.6/38.3PR-Annahme — 57/58 entschiedene PRs gemergt
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Verwendete Eingangsdaten
merged_prs57
open_issues3
closed_issues4
issue_closed_ratio0,571
closed_unmerged_prs1
Wie die Bewertung erfolgt
10/30Organisatorische Trägerschaft — persönliches (Nutzer-)Konto
0/20Verifizierte Domain — für Nutzerkonten nicht anwendbar
8.5/25Reichweite des Inhabers — 14 Follower von anatolykoptev
14.5/25Kontohistorie — 94 öffentliche Repos, Kontoalter ca. 0 Jahre
Verwendete Eingangsdaten
followers14
owner_typeUser
is_verified
owner_loginanatolykoptev
public_repos94
account_age_days276
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): Verifizierte Domain. Die verbleibenden Gewichte wurden renormalisiert.

Paketpflege

100Exzellent
Wie die Bewertung erfolgt
25/25Veröffentlicht & auflösbar — 1 Paket(e) auf go
35/35Veröffentlichungsaktualität — letzte Veröffentlichung vor 9 Tagen
20/20Versionshistorie — 72 veröffentlichte Versionen
20/20Nicht veraltet — aktiv, nicht veraltet oder zurückgezogen
Verwendete Eingangsdaten
packagesgithub.com/anatolykoptev/go-enriche
ecosystemsgo
any_deprecatednein
min_days_since_publish9

Engineering-Qualität

Sind grundlegende Engineering- und Dokumentationspraktiken vorhanden?

82Gut · 20 % des Gesamtindex
Wie die Bewertung erfolgt
24/24CI-Workflows — 1 Workflow(s)
24/24Tests vorhanden
16/16Linter-Konfiguration — .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
Verwendete Eingangsdaten
has_cija
has_testsja
has_editorconfignein
has_linter_configja
has_precommit_configja
Wie die Bewertung erfolgt
30/30README
25/25Dokumentationsverzeichnis
0/15Dokumentations-/Homepage-Site
0/10Repository-Beschreibung
10/10Topics — 5 Topics
0/10Wiki
Verwendete Eingangsdaten
topicsdata-enrichment, go, golang, mcp, mcp-server
has_wikinein
homepage
has_readmeja
has_docs_dirja
has_descriptionnein

Sicherheit

Sind die sichtbaren Sicherheits- und Lieferkettenpraktiken belastbar, ohne ungeklärte Exposition gegenüber Hochrisikojurisdiktionen?

48Gefährdet · 16 % des Gesamtindex

Sicherheitslage

48Gefährdet
Wie die Bewertung erfolgt
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.5Lizenz — 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 — keine Daten
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 — keine Daten
7.5/7.5Token-Permissions — GitHub workflow tokens follow principle of least privilege
6/7.5Vulnerabilities — 2 existing vulnerabilities detected
Verwendete Eingangsdaten
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4,8
Von der Bewertung ausgeschlossen (keine Daten oder nicht anwendbar): packaging, signed_releases. Die verbleibenden Gewichte wurden renormalisiert.

AI Readiness

Wie gut ist das Repository dafür ausgestattet, mit KI-Coding-Agenten entwickelt und gepflegt zu werden? Ein unabhängiges, experimentelles Badge — Gewicht 0,0, es wird eigenständig ausgewiesen und verändert den Gesamt-Gesundheitswert nicht.

70Gut · 0 % des Gesamtindex
Wie die Bewertung erfolgt
0/45Agentenanweisungen — keine CLAUDE.md / AGENTS.md / Editor-Regeln
0/15Maschinenlesbare Doku (llms.txt)
40/40Lesbare Commit-Historie — 100 von 100 menschlichen Commits benennen ihre Absicht (strukturierter Betreff oder erläuternder Text)
Verwendete Eingangsdaten
has_llms_txtnein
legible_history_share1
agent_instruction_files
agent_instruction_max_bytes
Wie die Bewertung erfolgt
18/18Bootstrap mit einem Befehl — Makefile
22/22Automatisierte Tests
11/11Lint-/Format-Konfiguration — .golangci.yml
11/11Statische Typprüfung — Go (statisch typisiert)
10/10Reproduzierbare Umgebung — lockfile
10/10Belegte Agentenpraxis — 47 der letzten 100 Commits von Agenten verfasst oder ihnen zugeschrieben
0/8Automatisierte Wartung — keine automatisierten Abhängigkeits-Updates beobachtet
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Verwendete Eingangsdaten
has_nixnein
has_testsja
lockfilesgo.sum
has_dockerfilenein
typed_languageja
bootstrap_filesMakefile
has_devcontainernein
has_linter_configja
typecheck_configs
agent_commit_share0,47
toolchain_manifestsgo.mod
dependency_bot_commit_share0
Wie die Bewertung erfolgt
45/45Typprüfbarer Code — Go (statisch typisiert)
55/55Handhabbare Dateigrößen — 0/154 Quelldateien über 60 KB
Verwendete Eingangsdaten
primary_languageGo
largest_source_bytes52.130
source_files_sampled154
oversized_source_files0

Eckdaten

0GitHub-Sterne
1Mitwirkende
151Commits, letzte 12 Monate
1Tage seit letztem Push
7Releases
1Bus-Faktor
3offene Issues
GoPaket-Ökosysteme

Warnungen zur Datenerhebung

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

Weitere Details

OpenSSF Scorecard 4.8 / 10
4.8Gesamtwert

Unabhängige, werkzeugneutrale Sicherheitsbewertung durch das quelloffene OpenSSF Scorecard. Jede Prüfung honoriert eine Sicherheits-Praxis, nicht das Werkzeug eines bestimmten Anbieters. Prüfungen, die Scorecard nicht ermitteln konnte, sind mit k. A. markiert und vom Sicherheitswert ausgeschlossen (nie als null gezählt).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
k. A.Packagingpackaging 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
k. A.Signed-Releasesno releases found
10Token-PermissionsGitHub workflow tokens follow principle of least privilege
8Vulnerabilities2 existing vulnerabilities detected
Direkte Abhängigkeiten 13
RegistryPaketVersionsvorgabeManifest
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
Alle Abhängigkeiten nicht erhoben

Der aufgelöste Abhängigkeitssatz konnte für diesen Bericht nicht erhoben werden: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

JSON-Rohbericht maschinenlesbar
{
  "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"
}

Bewertungen sind Signale, keine Garantien. Sie spiegeln öffentlich sichtbare Praxis auf GitHub wider — kein Code-Audit und keine Sicherheitsgarantie.

Fehlende Daten werden ausgeschlossen und die Gewichte neu normiert, nie als null bewertet. Die Methodik ist versioniert und offen: Metriken v1.13.0, Schema v0.27.0 — vollständige Methodik · Metriken-Wiki.

Wie ein einzelnes Ergebnis im Gesamtregister steht: aggregierte StatistikenGo.