Informe JSON sin procesar legible por máquina
{
"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"
}