公开记录
软件健康报告模式 0.26.0 · 指标 1.13.0 · 2026-07-22 01:59 UTC

sferik / multi_xml

A generic swappable back-end for XML parsing

RubyMIT★ 172 星标⑂ 38 复刻始于 2010年10月在 GitHub 上查看 ↗

sferik/multi_xml 的健康指数为 100 分中的 65 分,处于「中等」区间。 其得分最高的类别是Vitality(71/100),最低的是AI Readiness(32/100)。 最近一次更新在 8 天前。 近期的大部分工作由 1 位贡献者完成。

65
总分 / 100
中等

软件健康指数

指标归入加权类别,统一采用 1–100 量表。总体分先取类别加权平均;当公开证据触发高风险司法辖区政策时,评级会按政策调整,并设置 49(有风险)的上限。AI 就绪度不计入总体分。

65
优秀85-100堪称典范;基本满足所有检验标准
良好70-84健康;仅有轻微不足
中等50-69可接受,但存在明显不足;建议进行审查
存在风险30-49存在重大薄弱环节;采用时应保持审慎
危急1-29问题严重(项目被弃置、仅有单一维护者、缺乏基本工程规范)
活力社区与采用可持续性与治理工程质量安全AI 就绪度

评分画像

每条轴代表一个类别。形状比平均值更重要——健康的对象会填满整个图形,而“一峰一谷”式画像意味着某一维度的优势正掩盖另一维度的风险。

所有权

Erik Berlin个人账户
2,649 关注者216 个公开仓库始于 2008年5月

该仓库由个人账户拥有。相较于组织支持的项目,单一所有者项目的延续性风险更高。

软件包生态系统

注册表软件包版本月下载量版本数最近发布
RubyGemsmulti_xml0.9.1-2978 天前

按类别列示的指标

活力

项目是否仍有生命——是否仍在编写代码,是否仍在发布版本?

71良好 · 占总体的 22%
评分方式
28.8/36推送新近度 — 最近一次推送于 8 天前
6.2/36提交节奏 — 52 周中有 9 周有提交
17.1/18提交量 — 最近一年 80 次提交
10/10OpenSSF Scorecard:Maintained — 30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
所用输入
commits_last_year80
human_commit_share1
days_since_last_push8
active_weeks_last_year9

发布纪律

84良好
评分方式
27/27有发布版本 — 已发布 1 个发布版本
36/36发布时效 — 最近一次发布版本于 78 天前
12.6/27发布节奏 — 节奏未知(仅一次发布)
0/10OpenSSF Scorecard:Signed-Releases — 无数据
所用输入
releases_count1
latest_release_tagv0.9.1
releases_from_tags
days_since_latest_release78
mean_days_between_releases
已排除计分(无数据或不适用):OpenSSF Scorecard:Signed-Releases。 其余权重已重新归一化。

社区与采用

项目是否拥有用户、下载量与关注度,并具备欢迎贡献者参与的配置?

70良好 · 占总体的 18%
评分方式
36.2/60星标 — 172 个星标
13.1/25复刻 — 38 个复刻
2.7/15关注者 — 4 位关注者
所用输入
forks38
stars172
watchers4
growth_stateorganic
growth_factor_pct100

社区健康

70良好
评分方式
22.5/22.5README
22.5/22.5许可证 — 可识别的许可证(MIT)
18/18CONTRIBUTING 指南
0/13.5行为准则
0/7.2议题模板
0/6.3PR 模板
所用输入
has_readme
has_license
has_contributing
has_issue_template
has_code_of_conduct
has_pull_request_template
评分方式
80/80累计下载量 — rubygems 合计历史 583,709,139 次下载
0/20注册表被依赖数 — 该生态系统不报告此项
所用输入
packagesmulti_xml
dependents
ecosystemsrubygems
total_downloads583,709,139
monthly_downloads
已排除计分(无数据或不适用):注册表被依赖数。 其余权重已重新归一化。

可持续性与治理

项目能否在其成员之外延续——巴士系数、响应能力、由谁支持,以及软件包的维护状况?

68中等 · 占总体的 24%
评分方式
9/54巴士系数 — 1 位贡献者贡献了半数提交
2.7/22.5提交分布 — 头号贡献者编写了 88% 的提交
13.5/13.5贡献者广度 — 20 位贡献者
10/10OpenSSF Scorecard:Contributors — project has 16 contributing companies or organizations
所用输入
bus_factor1
contributors_sampled20
top_contributor_share0.879
评分方式
46.8/46.8议题解决 — 100% 的议题已关闭
30.8/38.3PR 接受 — 已裁定的 PR 中 33/41 已合并
0/15OpenSSF Scorecard:Code-Review — Found 0/30 approved changesets -- score normalized to 0
所用输入
merged_prs33
open_issues0
closed_issues34
issue_closed_ratio1
closed_unmerged_prs8
评分方式
10/30所有权背书 — 个人(用户)账户
0/20已验证域名 — 不适用于个人账户
24.6/25所有者影响力 — sferik 有 2,649 位关注者
25/25既往记录 — 216 个公开仓库,账户约 18 年
所用输入
followers2,649
owner_typeUser
is_verified
owner_loginsferik
public_repos216
account_age_days6,642
已排除计分(无数据或不适用):已验证域名。 其余权重已重新归一化。
评分方式
25/25已发布且可解析 — rubygems 上有 1 个软件包
35/35发布时效 — 最近一次发布于 78 天前
20/20版本历史 — 29 个已发布版本
20/20未被弃用 — 活跃,未被弃用或撤回
所用输入
packagesmulti_xml
ecosystemsrubygems
any_deprecated
min_days_since_publish78

工程质量

基础的工程与文档实践是否到位?

68中等 · 占总体的 20%

工程实践

80良好
评分方式
24/24CI 工作流 — 7 个工作流
24/24存在测试
16/16Linter 配置 — .rubocop.yml
0/9.6Pre-commit 钩子
0/6.4.editorconfig
0/20OpenSSF Scorecard:CI-Tests — 无数据
所用输入
has_ci
has_tests
has_editorconfig
has_linter_config
has_precommit_config
已排除计分(无数据或不适用):OpenSSF Scorecard:CI-Tests。 其余权重已重新归一化。

文档

50中等
评分方式
30/30README
0/25文档目录
0/15文档 / 主页站点
10/10仓库描述
0/10主题标签
10/10Wiki
所用输入
topics
has_wiki
homepage
has_readme
has_docs_dir
has_description

安全

可见的安全与供应链实践是否稳固,且不存在未解决的高风险司法辖区暴露?

45存在风险 · 占总体的 16%

安全态势

45存在风险
评分方式
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — 无数据
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
2.5/2.5Contributors — project has 16 contributing companies or organizations
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5许可证 — license file detected
7.5/7.5Maintained — 30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
5/5Packaging — packaging workflow detected
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — 无数据
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
所用输入
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4.5
已排除计分(无数据或不适用):ci_tests, signed_releases。 其余权重已重新归一化。

AI 就绪度

该仓库在多大程度上具备与 AI 编码代理协同开发与维护的条件?这是一枚独立的实验性徽章——权重为 0.0,因此单独呈现,不影响总体健康评分。

32存在风险 · 占总体的 0%
评分方式
0/45代理指令 — 没有 CLAUDE.md / AGENTS.md / 编辑器规则
0/15机器可读文档(llms.txt)
18.1/40可读的提交历史 — 100 次人类提交中有 34 次说明了意图(结构化标题或解释性正文)
所用输入
has_llms_txt
legible_history_share0.34
agent_instruction_files
agent_instruction_max_bytes
评分方式
0/18一条命令的引导启动
22/22自动化测试
11/11Lint / 格式化配置 — .rubocop.yml
0/11静态类型检查
0/10可复现环境
0/10已体现的代理实践 — 最近 100 次提交中没有代理编写的提交
0/8自动化维护 — 未观察到自动依赖更新
0/10OpenSSF Scorecard:Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
所用输入
has_nix
has_tests
lockfiles
has_dockerfile
typed_language
bootstrap_files
has_devcontainer
has_linter_config
typecheck_configs
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
评分方式
0/45可类型检查的代码 — Ruby,未配置类型检查
55/55可控的文件大小 — 采样的 80 个源文件中有 0 个超过 60KB
所用输入
primary_languageRuby
largest_source_bytes30,160
source_files_sampled80
oversized_source_files0

关键数据

172GitHub 星标
20贡献者
80最近 12 个月提交数
8距最近推送天数
1发布版本数
1巴士系数(bus factor)
0开放议题
RubyGems软件包生态系统数

数据采集警告

  • No resolved dependencies carried a version and a supported ecosystem

更多细节

Star 与 Fork 历史 172 ★ / 38 ⇿
172Star
38Fork
1发布

每颗 star 和每个 fork 的添加时间,来自 GitHub 并按天汇总。累计增长位于其构成来源——每日新增——的正上方,二者可相互对照:稳定的自然增长与短暂的突增形态截然不同。当这一差别可被衡量时,它会作为增长真实性予以报告。

0408012016020017237542010-102018-072026-05
主版本 0次版本 0修订 1

每个点涵盖 15 天。

OpenSSF Scorecard 4.5 / 10
4.5综合

来自开源项目 OpenSSF Scorecard 的独立、工具无关的安全评估。每项检查奖励的是安全实践本身,而非特定供应商的工具。Scorecard 无法判定的检查项标记为 不适用,并从安全评分中剔除(绝不按零分计)。Scorecard v5.5.0 · 2026-07-22 01:58 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
不适用CI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
10Contributorsproject has 16 contributing companies or organizations
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
10Packagingpackaging workflow detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
不适用Signed-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
直接依赖 22
注册表软件包版本约束清单文件
RubyGemslibxml-rubyGemfile
RubyGemsnokogiriGemfile
RubyGemsoga>= 2.3Gemfile
RubyGemsoxGemfile
RubyGemsrexmlGemfile
RubyGemsminitest>= 6Gemfile
RubyGemsminitest-mock>= 5.27Gemfile
RubyGemsmutant-minitest>= 0.14.1Gemfile
RubyGemsrake>= 13.3.1Gemfile
RubyGemsrbs>= 4.0.0Gemfile
RubyGemsrdoc>= 7.0.2Gemfile
RubyGemsrdoc>= 7.0.2Gemfile
RubyGemsrubocop>= 1.81.7Gemfile
RubyGemsrubocop-minitest>= 0.36Gemfile
RubyGemsrubocop-performance>= 1.26.1Gemfile
RubyGemsrubocop-rake>= 0.7.1Gemfile
RubyGemssimplecov>= 1Gemfile
RubyGemsstandard>= 1.52Gemfile
RubyGemsstandard-performance>= 1.9Gemfile
RubyGemssteep>= 2Gemfile
RubyGemsyard>= 0.9.38Gemfile
RubyGemsyardstick>= 0.9.9Gemfile
全部依赖 22

来自 GitHub 依赖图的完整解析依赖集合:21 个直接依赖与 1 个间接(传递)软件包。仓库提交锁文件时,传递闭包才是完整的。

注册表软件包版本关系
RubyGemslibxml-ruby直接
RubyGemsminitest直接
RubyGemsminitest-mock直接
RubyGemsmutant-minitest直接
RubyGemsnokogiri直接
RubyGemsoga直接
RubyGemsox直接
RubyGemsrake直接
RubyGemsrbs直接
RubyGemsrdoc直接
RubyGemsrexml直接
RubyGemsrubocop直接
RubyGemsrubocop-minitest直接
RubyGemsrubocop-performance直接
RubyGemsrubocop-rake直接
RubyGemssimplecov直接
RubyGemsstandard直接
RubyGemsstandard-performance直接
RubyGemssteep直接
RubyGemsyard直接
RubyGemsyardstick直接
RubyGemsbigdecimal间接
依赖安全公告 未评估

本报告未能完成公告比对:No resolved dependencies carried a version and a supported ecosystem

原始 JSON 报告 机器可读
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 575,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "Ruby": 258536,
        "Shell": 131
      },
      "pushed_at": "2026-07-13T21:04:12Z",
      "created_at": "2010-10-03T05:18:16Z",
      "owner_type": "User",
      "updated_at": "2026-07-13T21:04:49Z",
      "description": "A generic swappable back-end for XML parsing",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Ruby",
      "significant_languages": [
        "Ruby"
      ]
    },
    "owner": {
      "blog": null,
      "name": "Erik Berlin",
      "type": "User",
      "login": "sferik",
      "company": null,
      "location": "San Francisco",
      "followers": 2649,
      "avatar_url": "https://avatars.githubusercontent.com/u/10308?v=4",
      "created_at": "2008-05-14T20:36:12Z",
      "is_verified": null,
      "public_repos": 216,
      "account_age_days": 6642
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.9.1",
          "kind": "patch",
          "published_at": "2026-05-04T18:32:52Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "391efee2f509a7afde833eee8315f0ec7f59f16b",
          "body": "With the benchmark defaults of 0.03s warmup and 0.15s samples, the\nJIT engines are measured cold and close parser pairs flip: on\nTruffleRuby, nokogiri outranked oga by 21% in CI, while a warmed-up\nrun keeps the coded order (rexml, oga, nokogiri) within tolerance.\nGive the verify_preference job two seconds of warmup and one-second\nmeasurement windows so Graal and the JVM compile the hot paths\nbefore the ranking is asserted.",
          "is_bot": false,
          "headline": "Warm up the JIT before verifying parser preference order",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T20:48:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "54aacd371ddc7fe7bd5f6dec99724421a900b0b9",
          "body": "rdoc 8 depends on rbs 4, whose native extension cannot build on\nJRuby, and mutant pulls rdoc in via irb, so the existing platform\ngates on rbs and steep were bypassed; rdoc 7 has no rbs dependency.\nSkip the lowercase-require-path test on JRuby, where require-site\ncaching keeps warm call sites bound to the original Kernel#require\nso the stub never sees the call.",
          "is_bot": false,
          "headline": "Fix JRuby CI: cap rdoc below 8 and skip a require-stubbing test",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T11:46:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fd5955e32926847d96850b8d19525b01f7597047",
          "body": null,
          "is_bot": false,
          "headline": "Remove the redundant `keyword_init: true`",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T11:12:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c4ed4e7f22ef05acfc49df2d5bb540df64aa5e18",
          "body": "Collapse the 11 reopenings of MultiXMLBenchmark in benchmark.rb into\na single class body and extract the deprecated MultiXml constant shim\nfrom lib/multi_xml.rb into lib/multi_xml/legacy_constant.rb, required\nafter multi_xml/deprecated so the forwarder snapshot still sees the\ncomplete public API.",
          "is_bot": false,
          "headline": "Fix Style/OneClassPerFile offenses",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T11:10:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "046d7687e90a81f42cb9dbd86740401d1fd28563",
          "body": null,
          "is_bot": false,
          "headline": "Use to_h { ... } instead of each_with_object",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T04:29:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9acf8036f1d82ec74169c596dd1da7499c901b0e",
          "body": null,
          "is_bot": false,
          "headline": "Update SimpleCov to version 1.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-07-13T04:24:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "52a03525397b1b0b1a3bd49ec7bee09fdd7154c9",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.9.1",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-05-04T17:36:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1c34a5c51c32ec3a41bc6c232f8ee7a5d96463c0",
          "body": "Mirror multi_json's push.yml: after the gem is published to RubyGems,\ncreate a GitHub release for the tag with auto-generated notes. The\n`gh release view` check makes the step idempotent in case the workflow\nre-runs against an existing tag.",
          "is_bot": false,
          "headline": "Create a GitHub release on tag push",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-05-04T17:36:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "533d505f49cdd09c831a7d81270127714bfa77c2",
          "body": "The legacy MultiXml constant forwarded calls to MultiXML via\nmethod_missing, but Module#method doesn't consult method_missing, so\nMultiXml.method(:load) resolved to the inherited Kernel#load and\ncrashed with LoadError when invoked. Any caller capturing the legacy\nconstant's load method as a Method o\n[…]\nevery public method\nMultiXML exposes, so Module#method finds the forwarder directly. Drop\nthe now-dead method_missing and respond_to_missing? hooks; the eager\nforwarders cover the full public surface.",
          "is_bot": false,
          "headline": "Fix MultiXml.method(:load) resolving to Kernel#load",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-05-04T17:23:38Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f785ca1898ac0e9cb293b433dbffdf990e2021d3",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.9.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-05-02T16:39:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9164cc4bc566214ff6a4f3c5bbe80948b9d1f550",
          "body": "Three OxNamespaceTest collision tests fail intermittently on TruffleRuby\nwhen run in the full suite. The handler hash ends up empty\n(`{\"root\" => {}}`) which post-typecast collapses to `{\"root\" => nil}`,\nbreaking the assertion.\n\nRoot cause is TruffleRuby's JIT, not Ox: when earlier tests in the\nproce\n[…]\n_handling_test.rb to match the existing\nskip in parser_integration_test.rb, and filter `lib/multi_xml/parsers/ox.rb`\nfrom coverage on TruffleRuby so the missing lines don't trip the\nSimpleCov minimum.",
          "is_bot": false,
          "headline": "Skip Ox tests on TruffleRuby",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-05-02T16:39:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d4887e322b9f7fadf26ad8fed0630180b8aa2d44",
          "body": "The previous table reported absolute throughput numbers from a\nsingle MRI run, which went stale on every benchmark refresh and\ntold readers nothing about JRuby or TruffleRuby. The new table\nshows the actual PARSER_PREFERENCE ordering on each supported\nruntime, surfacing the TruffleRuby flip (rexml ahead of oga,\nnokogiri last) that the engine-specific constant encodes. CI\nkeeps it honest by failing if the observed ranking diverges.",
          "is_bot": false,
          "headline": "Replace benchmark scores in README with per-engine preference table",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T17:58:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "af45aad809b6b088878de9ee88c8959a4672d7d3",
          "body": "Adjacent parsers in PARSER_PREFERENCE whose observed throughput is\nwithin 10% of each other are now treated as tied, so a noisy run\nthat flips a close pair (oga vs nokogiri on TruffleRuby is 4% apart)\nno longer fails the check. Real ordering bugs -- e.g. preferring\nrexml over oga on MRI, where the g\n[…]\n Pairwise tolerance check on adjacent\npositions catches transitive violations too: if A > B > C in\npreference but actual scores have C significantly above A, the\nadjacent A-B or B-C check picks it up.",
          "is_bot": false,
          "headline": "Tolerate noise in PARSER_PREFERENCE verifier",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T17:37:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d6f805eabb3ca0550642292aa6368502e8d0da15",
          "body": "TruffleRuby's JIT inverts the FFI-vs-pure-Ruby tradeoff: rexml jumps\nto the head of auto-detection (after ox, which is filtered out by\nskip_on_platform?) and nokogiri falls to last. Pin a TruffleRuby\nordering of rexml > libxml > oga > nokogiri behind a RUBY_ENGINE\ncheck so the verifier passes on all\n[…]\nocov:) wrap the conditional because the\nunreachable branch on a given engine would otherwise drop branch\ncoverage below the 100% MRI threshold.\n\nAlso adds truffleruby to the benchmark workflow matrix.",
          "is_bot": false,
          "headline": "Add TruffleRuby-specific PARSER_PREFERENCE",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T17:37:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "41430029f529cd8c0d50539614ce9ae2f466f57e",
          "body": "Adds jruby-10.0 to the benchmark workflow matrix. JRuby's available\nparser set (no ox/libxml; oga crashes on JRuby 10) shrinks the\nfiltered comparison to nokogiri vs rexml, which still matches\nPARSER_PREFERENCE -- so a single shared constant works for both\nruntimes.",
          "is_bot": false,
          "headline": "Verify PARSER_PREFERENCE on JRuby in addition to MRI",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:22:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d77088470f9822e503e7e2e7fa60740e95c9c89b",
          "body": "Adds a --verify-preference flag to benchmark.rb and a CI workflow\nthat runs the benchmark and asserts the throughput ranking, filtered\nto parsers in MultiXML::PARSER_PREFERENCE, matches the constant's\norder. Missing parsers (e.g. ox unavailable on JRuby) are tolerated.\nThe check pins to ubuntu-latest + Ruby 4.0 since benchmark numbers\naren't comparable across runtimes.",
          "is_bot": false,
          "headline": "Verify PARSER_PREFERENCE matches benchmark ranking in CI",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:20:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "182fa34e5b7635c5cfac07e45b1ee7e99da190b2",
          "body": "Swap oga ahead of rexml so the constant's \"fastest first\" promise is\nhonest: oga (0.168) outperforms rexml (0.117) on the bundled\nbenchmark suite. Affects auto-detection only when none of ox,\nlibxml-ruby, or nokogiri are installed; explicit parser selection\nis unchanged.",
          "is_bot": false,
          "headline": "Reorder PARSER_PREFERENCE to match benchmark throughput",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:20:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8b1c84bb317c26cf573f30ec1a597b5058dd878b",
          "body": "Re-ran the suite on Ruby 4.0 / Apple Silicon. libxml edges past\nnokogiri_sax for the #2 slot; other relative positions unchanged.\nAlso drops the \":parsers\" namespace from the rake invocation to\nmatch the renamed task.",
          "is_bot": false,
          "headline": "Refresh benchmark ranking table in README",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:20:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9c0e7e1c294853120f6dd4c0551bd2daf609f7aa",
          "body": "Drops the namespace and the alias since there's only one benchmark\ntask. Invocation simplifies to \"rake benchmark\"; BENCHMARK_ARGS still\nforwards to benchmark.rb.",
          "is_bot": false,
          "headline": "Promote benchmark:parsers to a top-level rake benchmark task",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:06:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b4c127d42f7ccdb4ea747014f34dc5874134cb4f",
          "body": "Oga's Java backend crashes on JRuby 10 with java.lang.NoSuchMethodError,\nwhich extends java.lang.Error and bypasses StandardError rescues --\naborting the whole benchmark instead of letting the parser get excluded.\n\nCatch Java::JavaLang::Throwable alongside StandardError on JRuby so the\nexisting excluded-parsers path handles backend incompatibilities the\nsame way it handles output mismatches and Ruby exceptions.",
          "is_bot": false,
          "headline": "Skip JRuby-incompatible parsers in benchmark validation",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-30T16:01:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fe000beffa239b20e0e8ab982c706adca67ccc13",
          "body": null,
          "is_bot": false,
          "headline": "Add regression test for mixed-content inline text",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6e14d5bb7cd25f9d01915b498a58c2795308d4cf",
          "body": null,
          "is_bot": false,
          "headline": "Add parser benchmark and document benchmark ranking",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "15265c39ebc5d790ffbd36816f78fdb95fdb9057",
          "body": null,
          "is_bot": false,
          "headline": "Fix Ox mixed-content text aggregation in SAX parser",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9343839f5464ff5771cd760be02c7dc7e68f1dfb",
          "body": "Add a shared Parser mixin that reads the ParseError constant off the\nextending module via const_get with inherit: false (so stray top-level\n::ParseError doesn't leak through). Every built-in parser extends it\nand declares its backend exception class as ParseError, matching the\nMultiJSON::Adapter convention.\n\nCustom parsers can either extend Parser + define a ParseError constant\nor keep defining a .parse_error method directly; validate_parser!\naccepts both styles.",
          "is_bot": false,
          "headline": "Introduce MultiXML::Parser base module with ParseError constant",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bc34f7e2ac5b0160b23f34d293489a415aafb1be",
          "body": "Raised when the parser spec is invalid, requiring the parser file\nraises LoadError, or the resolved parser doesn't satisfy the contract\n(must respond to .parse and define either a ParseError constant or a\n.parse_error method). Inherits from ArgumentError and carries the\noriginal exception's class name in its message, matching the role of\nMultiJSON::AdapterError.",
          "is_bot": false,
          "headline": "Add MultiXML::ParserLoadError for parser selection failures",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ba338d40718d79e06c2b28b817b82361eeec16a6",
          "body": "Introduce process-wide default options that merge between DEFAULT_OPTIONS\nand call-site overrides. Accepts a Hash or a callable (Proc/lambda); a\ncallable receives the call-site hash as its sole positional argument so\ndefaults can be computed per-call. Matches MultiJSON.parse_options.",
          "is_bot": false,
          "headline": "Add MultiXML.parse_options / .parse_options= for global defaults",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e76301065c24699a656e37d541f9008e64fea4c4",
          "body": "The override lives in Fiber[:multi_xml_parser] so concurrent fibers\nand threads each see their own parser without racing on a shared\nmodule variable; nested calls save and restore the previous\nfiber-local value. Matches MultiJSON.with_adapter.",
          "is_bot": false,
          "headline": "Add MultiXML.with_parser for fiber-local scoped overrides",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a5c53bbdb46eef4e37b48cd1156c3a413c62478f",
          "body": "Add JRuby 10.0 and TruffleRuby 33.0 (native and JVM) to the CI matrix,\nmatching the test coverage of MultiJSON. TruffleRuby is\nexcluded from Windows runners since the setup-ruby action doesn't\nsupport it there.",
          "is_bot": false,
          "headline": "Test against JRuby and TruffleRuby",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-29T01:18:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5aa5fcbe80290935d05921c2c34dff0bbf2e3fb5",
          "body": "Match Ruby stdlib's JSON.parse and MultiJSON. The\nlegacy :symbolize_keys option continues to work and emits a one-time\ndeprecation warning; it will be removed in 1.0. When both names appear\ntogether, :symbolize_names wins and :symbolize_keys is silently dropped.",
          "is_bot": false,
          "headline": "Rename :symbolize_keys option to :symbolize_names",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T20:06:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ce00fa7bb4581ef1b8048e605c32c9c2a822a2df",
          "body": "Adopt the MultiJSON README layout: CI badges, intro paragraph, minimal\nusage example, error-handling block, deprecation table for 0.9.0, and\ncustom-parser section. Drop the \"Drop-in replacement for stdlib\" and\n\"Gem Variants\" sections since neither applies to MultiXML.",
          "is_bot": false,
          "headline": "Restructure README to match MultiJSON",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T20:06:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "da358245bfbd3957a7e9466ecc648b0f6f5cf0cf",
          "body": "Match the MultiJSON.load → MultiJSON.parse convention. MultiXML.load\ncontinues to work and emits a one-time deprecation warning; it will\nbe removed in 1.0.",
          "is_bot": false,
          "headline": "Add a deprecated load alias for parse",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T20:06:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9099c3336a703ab942ee356a8f1a0456003fb50f",
          "body": "Use the all-caps acronym matching sister library MultiJSON. The legacy\nMultiXml constant continues to resolve via method_missing and\nconst_missing with a one-time deprecation warning, so every existing\ncall site keeps working through the 0.x line.",
          "is_bot": false,
          "headline": "Rename MultiXml to MultiXML and deprecate MultiXml",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:05:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2dee313b83db33b071cd4eb21585a7f5793a8bd7",
          "body": "When attributes with different namespace prefixes strip to the same\nlocal name (e.g. `foo:id` and `bar:id` both become `id`), collect their\nvalues in an array while preserving document order and keeping attribute\nvalues ahead of any colliding child elements. The libxml SAX parser\nfalls back to the DOM backend in this case since its callback drops\nprefixes from attribute qnames.",
          "is_bot": false,
          "headline": "Handle namespaced attribute name collisions consistently",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f0eab96d04f223bf17b21443ec033dcd760a8b1b",
          "body": null,
          "is_bot": false,
          "headline": "Fix parser detection",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ae0e9d109287cc7e62c140985305399fadcc9579",
          "body": null,
          "is_bot": false,
          "headline": "Harden namespace parsing and extract parse helpers",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9c1313ea794518831253ec33e320c403f352715b",
          "body": "Add a :namespaces option to MultiXml.parse that makes every backend\nproduce byte-identical output:\n\n* :strip (default) — drop xmlns declarations and prefixes, matching\n  today's libxml/nokogiri behavior\n* :preserve — keep source prefixes (\"atom:rel\") and xmlns decls",
          "is_bot": false,
          "headline": "Fix inconsistent namespace handling across parsers",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1d930a0fe3545b342e934764e168a19c3349118f",
          "body": null,
          "is_bot": false,
          "headline": "Ignore .mutant directory",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "27fd3bb59f79a0f746256afa041d03b890fa14a6",
          "body": "libxml-ruby 6.0.0 removed the deprecated `libxml.rb` shim, so\n`require \"libxml\"` now raises LoadError. Switch to the canonical\n`require \"libxml-ruby\"`, which is present in both 5.x and 6.x.\n\nAlso bump steep to >= 2 so CI resolves against rbs 4.x.",
          "is_bot": false,
          "headline": "Support libxml-ruby 6.0.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "11dba85e7d051623d770915a61f25927abd93a3f",
          "body": "```\nFinished in 0.050951s, 12600.3415 runs/s, 18586.4851 assertions/s.\n\n  1) Error:\nLoadParserCamelizeTest#test_load_parser_handles_underscore_names:\nLoadError: cannot load such file -- libxml\n    /Users/sue445/.rbenv/versions/4.0.2/lib/ruby/4.0.0/bundled_gems.rb:60:in 'Kernel.require'\n    /Users/su\n[…]\n_xml.rb:110:in 'MultiXml.load_parser'\n    test/load_parser_test.rb:56:in 'LoadParserCamelizeTest#test_load_parser_handles_underscore_names'\n\n642 runs, 947 assertions, 0 failures, 1 errors, 0 skips\n```",
          "is_bot": false,
          "headline": "Fix error with libxml-ruby 6.0.0",
          "author_name": "sue445",
          "author_login": "sue445",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "32d0dc255b22bbfb4318d02699950181490ca552",
          "body": "…ith rbs v4\n\n```\nlib/multi_xml/errors.rb:1:0: [error] Type checking failed due to error in library RBS file\n│ Different superclasses are specified for `::Psych::SyntaxError`\n│ (/Users/sue445/workspace/github.com/sferik/multi_xml/vendor/bundle/ruby/4.0.0/gems/rbs-4.0.2/stdlib/psych/0/psych.rbs:434:2)\n│\n│ Diagnostic ID: Ruby::LibraryRBSError\n│\n└ module MultiXml\n  ~~~~~~~~~~~~~~~\n```",
          "is_bot": false,
          "headline": "Fix Different superclasses are specified for `::Psych::SyntaxError` w…",
          "author_name": "sue445",
          "author_login": "sue445",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c4aee68fe0a7b55950033302fb7ca2a643dca99f",
          "body": null,
          "is_bot": false,
          "headline": "Test on Ruby 4.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-04-28T18:04:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c016f910f3fb35dcb050d7a5393b1773bae44bc0",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.8.1",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-01-08T14:39:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7c8977fb78590930359fae0e5082a821aa342d06",
          "body": "Closes https://github.com/sferik/multi_xml/issues/73.",
          "is_bot": false,
          "headline": "Fix array unwrapping when elements contain nil",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-01-08T06:38:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "75ea04280c5f4294a0e409fef5d3c3767148234e",
          "body": null,
          "is_bot": false,
          "headline": "Update to Minitest 6",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2026-01-06T04:26:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "df3bcded2db18c5d2eb961c393860f7ab02f07a6",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.8.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T14:09:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1d87b8f0e2dfa4d08276b671ef68b13aa5c8f71d",
          "body": null,
          "is_bot": false,
          "headline": "Add push workflow and update release rake task",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T14:09:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cea6be3e3770dccab173d305bfacb97ed9050717",
          "body": null,
          "is_bot": false,
          "headline": "Document Ruby 4 support",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0a3bd225648181a32cdef85a75459de3ecbc761c",
          "body": null,
          "is_bot": false,
          "headline": "Refactor",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f5d3655f6d944968256ef4280f1b1a2d0286a2a6",
          "body": null,
          "is_bot": false,
          "headline": "Remove test skips",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e43f18848d90fe79fe4a33ad2eac94764ef0c2af",
          "body": null,
          "is_bot": false,
          "headline": "Increase branch coverage to 100%",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6811d856c0e30258b7290d8f0eb5c2fefeee34da",
          "body": null,
          "is_bot": false,
          "headline": "Fix tests on Windows",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eaa386cfef4ebb679e78118cb17f2ffb26a7d258",
          "body": null,
          "is_bot": false,
          "headline": "Drop support for JRuby",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eaaff8b9b5452598b6421f4d654e14d090b40c7f",
          "body": null,
          "is_bot": false,
          "headline": "Add type checking with RBS and Steep",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "49702d92a9ebc8e6584e5d24833c1d9ecd671268",
          "body": null,
          "is_bot": false,
          "headline": "More refactoring",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2a0ff51f426079b0a1baac363efa437baca1bf13",
          "body": null,
          "is_bot": false,
          "headline": "More test cleanup",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2cca440fc13daa0255af0f5743ed57973888e834",
          "body": null,
          "is_bot": false,
          "headline": "Kill remaining mutants",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61ef966dde3808ccbecbbca27e2d676f7a061e5d",
          "body": null,
          "is_bot": false,
          "headline": "Cleanup tests",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be8df358efea132db3f1cc18518251e535c9c181",
          "body": null,
          "is_bot": false,
          "headline": "Kill mutants",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18144f88b1bf1a2d639bdf2450d7397ce5ce45cb",
          "body": null,
          "is_bot": false,
          "headline": "Suppress oga warnings",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "661b419703cfe50b3d7f63431c91b6cacbab3a0d",
          "body": null,
          "is_bot": false,
          "headline": "Add mutant",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9fe7948112879b3d449be2bca2ef4194b73f09cd",
          "body": null,
          "is_bot": false,
          "headline": "100% c0 code coverage",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c58286849951cd2f6cac2d9b19d43104e165a520",
          "body": null,
          "is_bot": false,
          "headline": "Covert from RSpec to Minitest",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "885ff010de9a46218d93ce378e896268b208d0a9",
          "body": null,
          "is_bot": false,
          "headline": "100% documentation coverage",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "78a715f7232496ecbefde5ced529fadfc4a508e8",
          "body": null,
          "is_bot": false,
          "headline": "More refactoring",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5ac2c349a59651872bbcf4ae3a4d9b03fff5c06c",
          "body": null,
          "is_bot": false,
          "headline": "Extract modules and constants into separate files",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "156fc5c0b9be47285dca8084fc6bc3a7c2e5d75b",
          "body": null,
          "is_bot": false,
          "headline": "Update workflows",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2a78cb3f451851a9d78ddfa03ea2bf3834028ca8",
          "body": null,
          "is_bot": false,
          "headline": "Extract global variables into SpeedTest module",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "81c4fd8f3dbb1d2becdc328a3f862c64fde23b18",
          "body": null,
          "is_bot": false,
          "headline": "Replace @xml instance variables with let(:xml) declarations",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c3fadd0acaf28939e59b08285fbb7213564cd824",
          "body": null,
          "is_bot": false,
          "headline": "Refactor",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6127e278a879391a247573cdd4abb4855421e26c",
          "body": null,
          "is_bot": false,
          "headline": "Refactor code into Helpers module",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e0530a935ad1677792ff1348c8faf6b3a73ca429",
          "body": null,
          "is_bot": false,
          "headline": "rake standard:fix",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "695729f7103305ed852da1efbb493504d5db773c",
          "body": "Resolves https://github.com/sferik/multi_xml/issues/32.",
          "is_bot": false,
          "headline": "Make parsing errors inspectable with cause and xml accessors",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ade3c5005ae76c4ca835186c25dac9c3a580e481",
          "body": "Previously, parsers handled whitespace-only content differently:\n  - nokogiri, libxml, oga: always stripped whitespace\n  - ox, rexml: always preserved whitespace\n\nNow all parsers have the same behavior:\n  - Empty elements return nil\n  - Whitespace-only content with no children/attributes is preserve\n[…]\n>\"        -> {\"tag\" => \" \"}\n    \"<tag> <child/></tag>\" -> {\"tag\" => {\"child\" => nil}}\n    \"<tag attr='x'> </tag>\" -> {\"tag\" => {\"attr\" => \"x\"}}\n\nResolves https://github.com/sferik/multi_xml/issues/46.",
          "is_bot": false,
          "headline": "Fix inconsistent whitespace handling across parsers",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "663ac9d3325146ca5306c5db1ff4d33ac55d65e8",
          "body": "Use top-level constant references (::Ox, ::LibXML, etc.) in the\n`defined?` checks to ensure we check for the actual gem constants rather\nthan the local parser module constants.\n\nPreviously, `defined?(Ox)` inside `MultiXml::Parsers::Ox` would find the\nparser module itself due to Ruby's constant looku\n[…]\nn't loaded. This\nresulted in \"undefined method `sax_parse' for Ox:Module\" errors in\nmultithreaded environments or when the gem wasn't preloaded.\n\nResolve https://github.com/sferik/multi_xml/issues/53.",
          "is_bot": false,
          "headline": "Fix constant lookup in parser require guards",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5dc70562a003eb7f621eeb46770090ec8255a3ed",
          "body": "Implement experimental event-driven SAX parsing alternatives to the\nexisting DOM-based parsers. SAX parsing can be more memory-efficient for\nlarge XML documents since it doesn't build the entire DOM tree in memory.\n\nUsage:\n    MultiXml.parser = :nokogiri_sax  # Uses Nokogiri::XML::SAX::Parser\n    Mu\n[…]\nracter references that\n    SAX parsers return differently than DOM parsers\n  - Both parsers properly raise MultiXml::ParseError for invalid XML\n\nResolves https://github.com/sferik/multi_xml/issues/10.",
          "is_bot": false,
          "headline": "Add SAX parsers for Nokogiri and LibXML",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c7f532af83963beb0204fcc5a4419cb1fdcbbe82",
          "body": null,
          "is_bot": false,
          "headline": "Change bigdecimal dependency to >= 3.1, < 5",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-24T13:10:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f5929bd870a94c7b9ee7cb99d4cfd33d87ca05a7",
          "body": "This reverts commit ecc425a26b5c2108732dfbf7d68e82cd7e16aec9.",
          "is_bot": false,
          "headline": "Revert \"Pass symbol as an argument to proc instead of a block\"",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-20T16:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c94bbfa4722f0356c111cc313baa8449817c31ae",
          "body": "Allow specifying a parser for individual parse operations via the\n:parser option, enabling use of different parsers in the same\napplication without changing the class-level setting.\n\nThe option accepts symbols, strings, or parser modules, consistent with\nthe existing MultiXml.parser= API.\n\nResolves https://github.com/sferik/multi_xml/issues/63.",
          "is_bot": false,
          "headline": "Add per-parse :parser option to MultiXml.parse",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-20T07:32:51Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ecc425a26b5c2108732dfbf7d68e82cd7e16aec9",
          "body": null,
          "is_bot": false,
          "headline": "Pass symbol as an argument to proc instead of a block",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-20T06:37:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8beccc67a578e2fc6ee269bbf464f425621ba9de",
          "body": "Resolves https://github.com/sferik/multi_xml/issues/72.",
          "is_bot": false,
          "headline": "Update bigdecimal depdnency to ~> 4.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-12-20T06:36:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "338107755fab44ef425225a03d5bc2b0640e436d",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.7.2",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-30T18:30:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "120fef648e702b903109eb001ef78c22585da616",
          "body": null,
          "is_bot": false,
          "headline": "Update CI workflow",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T20:57:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7946028f70d4b41bfc967e3fdf3ec0e8f882ba7e",
          "body": null,
          "is_bot": false,
          "headline": "Update LICENSE and README",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T20:03:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "00b3fca87f1eaa2cb3caefa1553684203278679d",
          "body": null,
          "is_bot": false,
          "headline": "Add metadata hash",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T20:03:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f7708e14c9e8fda165d5172d110d2c2e708520ea",
          "body": null,
          "is_bot": false,
          "headline": "Drop support for Ruby 3.1",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T19:40:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "70dab39a5597a2ff5bd535b0c30b0ce892cf9db3",
          "body": null,
          "is_bot": false,
          "headline": "Use add_dependency instead of add_runtime_dependency",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T19:37:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4951afefae166bfbff2a35fd276abbfc41eeb693",
          "body": null,
          "is_bot": false,
          "headline": "Specify plugins instead of require",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2025-04-29T19:32:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "854e26c603972ecf9898b99e7092a69fe619d6c9",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.7.1",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-05-01T18:50:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d81665f1ce7cd1cad07cfab3f19a679c4fdad002",
          "body": "…n stable\n\nSee discussion at https://github.com/sferik/multi_xml/commit/712cf97da3e6385179214644452cb7775d14e7d8#r141554010.",
          "is_bot": false,
          "headline": "Relax required Ruby version constraint to allow installation on Debia…",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-05-01T18:49:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1a2cde755452773aced4a870ff963a181a14381a",
          "body": null,
          "is_bot": false,
          "headline": "Bump version to 0.7.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T19:08:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2b7e1637f800014bb7eaee6ed7dbffb5e08fa938",
          "body": null,
          "is_bot": false,
          "headline": "Add bin for development",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T19:00:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "712cf97da3e6385179214644452cb7775d14e7d8",
          "body": null,
          "is_bot": false,
          "headline": "Drop support for Ruby 3.0",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a2be7ce1ca13eb4b37d8ac88f6cbb893f18eccd2",
          "body": null,
          "is_bot": false,
          "headline": "Update runtime dependency in gemspec",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "86fdc12fa4a4e2a588abb768589703fe6e7e2f38",
          "body": null,
          "is_bot": false,
          "headline": "Update development dependencies in Gemfile",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1659443ea2659e7539e8bca2797dc3670e065985",
          "body": null,
          "is_bot": false,
          "headline": "Clean up code",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b832249e270db9cd957d1a2e7c978400f4db960",
          "body": null,
          "is_bot": false,
          "headline": "Update copyright year",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b79adf03a93a4bb7cead2ab0752f54addad9c5df",
          "body": null,
          "is_bot": false,
          "headline": "Update name",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:59:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d7ea5225f3e10c2aeb95e6d24235c6de1487cff2",
          "body": null,
          "is_bot": false,
          "headline": "Clean up code",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:37:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f2ea10a7acedc044398d3ad11586b87dfb54d1ad",
          "body": null,
          "is_bot": false,
          "headline": "Use parentheses for super with arguments",
          "author_name": "Erik Berlin",
          "author_login": "sferik",
          "committed_at": "2024-04-30T18:33:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "54ff3221d241819d0183985494ef63126319f407",
          "body": null,
          "is_bot": false,
          "headline": "Add ruby 3.3 to CI",
          "author_name": "Earlopain",
          "author_login": "Earlopain",
          "committed_at": "2024-04-30T18:29:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 1,
      "commits_last_year": 80,
      "latest_release_at": "2026-05-04T18:32:52Z",
      "latest_release_tag": "v0.9.1",
      "releases_from_tags": false,
      "days_since_last_push": 8,
      "active_weeks_last_year": 9,
      "days_since_latest_release": 78,
      "mean_days_between_releases": null
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 57,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "multi_xml",
          "exists": true,
          "license": "MIT",
          "keywords": [],
          "ecosystem": "rubygems",
          "matches_repo": true,
          "registry_url": "https://rubygems.org/gems/multi_xml",
          "is_deprecated": false,
          "latest_version": "0.9.1",
          "repository_url": "https://github.com/sferik/multi_xml",
          "versions_count": 29,
          "total_downloads": 583709139,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": "2010-10-03T05:57:04.117000Z",
          "latest_published_at": "2026-05-04T18:32:12.994000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 78
        }
      ]
    },
    "popularity": {
      "forks": 38,
      "stars": 172,
      "watchers": 4,
      "fork_history": {
        "days": [
          {
            "date": "2010-10-04",
            "count": 1
          },
          {
            "date": "2011-06-05",
            "count": 1
          },
          {
            "date": "2011-08-24",
            "count": 1
          },
          {
            "date": "2011-12-30",
            "count": 1
          },
          {
            "date": "2012-04-16",
            "count": 1
          },
          {
            "date": "2012-05-08",
            "count": 1
          },
          {
            "date": "2013-01-11",
            "count": 1
          },
          {
            "date": "2013-05-31",
            "count": 1
          },
          {
            "date": "2013-07-02",
            "count": 1
          },
          {
            "date": "2013-07-08",
            "count": 1
          },
          {
            "date": "2013-07-12",
            "count": 1
          },
          {
            "date": "2013-08-14",
            "count": 1
          },
          {
            "date": "2013-09-02",
            "count": 1
          },
          {
            "date": "2013-12-27",
            "count": 1
          },
          {
            "date": "2014-02-10",
            "count": 1
          },
          {
            "date": "2015-02-10",
            "count": 1
          },
          {
            "date": "2015-02-14",
            "count": 1
          },
          {
            "date": "2015-06-18",
            "count": 1
          },
          {
            "date": "2015-10-16",
            "count": 1
          },
          {
            "date": "2015-11-02",
            "count": 1
          },
          {
            "date": "2016-03-24",
            "count": 1
          },
          {
            "date": "2016-07-24",
            "count": 1
          },
          {
            "date": "2016-12-05",
            "count": 1
          },
          {
            "date": "2017-04-06",
            "count": 1
          },
          {
            "date": "2017-05-18",
            "count": 1
          },
          {
            "date": "2017-06-22",
            "count": 1
          },
          {
            "date": "2019-02-01",
            "count": 1
          },
          {
            "date": "2019-03-04",
            "count": 1
          },
          {
            "date": "2020-07-28",
            "count": 1
          },
          {
            "date": "2021-01-12",
            "count": 1
          },
          {
            "date": "2022-02-12",
            "count": 1
          },
          {
            "date": "2023-06-02",
            "count": 1
          },
          {
            "date": "2024-01-02",
            "count": 1
          },
          {
            "date": "2024-02-13",
            "count": 1
          },
          {
            "date": "2024-05-11",
            "count": 1
          },
          {
            "date": "2024-06-09",
            "count": 1
          },
          {
            "date": "2026-04-17",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 37,
        "total_forks": 38
      },
      "star_history": {
        "days": [
          {
            "date": "2010-10-03",
            "count": 52
          },
          {
            "date": "2010-10-05",
            "count": 2
          },
          {
            "date": "2011-03-10",
            "count": 1
          },
          {
            "date": "2011-06-13",
            "count": 1
          },
          {
            "date": "2011-06-20",
            "count": 1
          },
          {
            "date": "2011-06-29",
            "count": 1
          },
          {
            "date": "2013-02-20",
            "count": 1
          },
          {
            "date": "2013-03-07",
            "count": 1
          },
          {
            "date": "2013-03-09",
            "count": 1
          },
          {
            "date": "2013-04-18",
            "count": 1
          },
          {
            "date": "2013-04-20",
            "count": 1
          },
          {
            "date": "2013-04-22",
            "count": 1
          },
          {
            "date": "2013-06-10",
            "count": 1
          },
          {
            "date": "2013-06-17",
            "count": 1
          },
          {
            "date": "2013-06-27",
            "count": 1
          },
          {
            "date": "2013-07-04",
            "count": 1
          },
          {
            "date": "2013-07-08",
            "count": 1
          },
          {
            "date": "2013-10-01",
            "count": 1
          },
          {
            "date": "2013-10-12",
            "count": 1
          },
          {
            "date": "2013-10-30",
            "count": 1
          },
          {
            "date": "2013-11-14",
            "count": 1
          },
          {
            "date": "2014-01-01",
            "count": 1
          },
          {
            "date": "2014-01-03",
            "count": 1
          },
          {
            "date": "2014-01-08",
            "count": 1
          },
          {
            "date": "2014-04-03",
            "count": 1
          },
          {
            "date": "2014-04-17",
            "count": 1
          },
          {
            "date": "2014-04-20",
            "count": 1
          },
          {
            "date": "2014-04-23",
            "count": 1
          },
          {
            "date": "2014-06-07",
            "count": 1
          },
          {
            "date": "2014-07-21",
            "count": 1
          },
          {
            "date": "2014-07-31",
            "count": 1
          },
          {
            "date": "2014-08-01",
            "count": 1
          },
          {
            "date": "2014-08-31",
            "count": 1
          },
          {
            "date": "2014-09-01",
            "count": 1
          },
          {
            "date": "2014-09-24",
            "count": 1
          },
          {
            "date": "2014-11-21",
            "count": 1
          },
          {
            "date": "2014-12-19",
            "count": 1
          },
          {
            "date": "2015-02-07",
            "count": 1
          },
          {
            "date": "2015-03-04",
            "count": 1
          },
          {
            "date": "2015-05-19",
            "count": 1
          },
          {
            "date": "2015-07-23",
            "count": 1
          },
          {
            "date": "2015-08-08",
            "count": 1
          },
          {
            "date": "2015-08-11",
            "count": 1
          },
          {
            "date": "2015-09-21",
            "count": 1
          },
          {
            "date": "2015-10-15",
            "count": 1
          },
          {
            "date": "2015-11-05",
            "count": 1
          },
          {
            "date": "2015-11-09",
            "count": 2
          },
          {
            "date": "2015-11-24",
            "count": 1
          },
          {
            "date": "2015-12-21",
            "count": 1
          },
          {
            "date": "2016-02-18",
            "count": 1
          },
          {
            "date": "2016-02-26",
            "count": 2
          },
          {
            "date": "2016-03-11",
            "count": 1
          },
          {
            "date": "2016-04-06",
            "count": 1
          },
          {
            "date": "2016-04-15",
            "count": 1
          },
          {
            "date": "2016-06-26",
            "count": 1
          },
          {
            "date": "2016-07-20",
            "count": 2
          },
          {
            "date": "2016-08-10",
            "count": 1
          },
          {
            "date": "2016-08-16",
            "count": 1
          },
          {
            "date": "2016-09-28",
            "count": 1
          },
          {
            "date": "2016-11-04",
            "count": 1
          },
          {
            "date": "2016-12-11",
            "count": 1
          },
          {
            "date": "2016-12-19",
            "count": 1
          },
          {
            "date": "2017-02-27",
            "count": 1
          },
          {
            "date": "2017-03-09",
            "count": 1
          },
          {
            "date": "2017-04-03",
            "count": 1
          },
          {
            "date": "2017-04-08",
            "count": 1
          },
          {
            "date": "2017-04-10",
            "count": 1
          },
          {
            "date": "2017-05-17",
            "count": 1
          },
          {
            "date": "2017-06-16",
            "count": 1
          },
          {
            "date": "2017-06-27",
            "count": 1
          },
          {
            "date": "2017-07-27",
            "count": 1
          },
          {
            "date": "2017-10-04",
            "count": 1
          },
          {
            "date": "2017-12-23",
            "count": 1
          },
          {
            "date": "2018-02-12",
            "count": 1
          },
          {
            "date": "2018-02-16",
            "count": 1
          },
          {
            "date": "2018-02-26",
            "count": 1
          },
          {
            "date": "2018-04-28",
            "count": 1
          },
          {
            "date": "2018-06-20",
            "count": 1
          },
          {
            "date": "2018-08-03",
            "count": 1
          },
          {
            "date": "2018-10-03",
            "count": 1
          },
          {
            "date": "2018-11-06",
            "count": 1
          },
          {
            "date": "2019-01-09",
            "count": 1
          },
          {
            "date": "2019-02-01",
            "count": 1
          },
          {
            "date": "2019-03-07",
            "count": 1
          },
          {
            "date": "2019-03-31",
            "count": 1
          },
          {
            "date": "2019-05-17",
            "count": 1
          },
          {
            "date": "2019-07-10",
            "count": 1
          },
          {
            "date": "2019-08-11",
            "count": 1
          },
          {
            "date": "2020-02-19",
            "count": 1
          },
          {
            "date": "2020-06-16",
            "count": 1
          },
          {
            "date": "2020-07-22",
            "count": 1
          },
          {
            "date": "2020-08-29",
            "count": 1
          },
          {
            "date": "2022-01-31",
            "count": 1
          },
          {
            "date": "2023-06-02",
            "count": 1
          },
          {
            "date": "2023-06-24",
            "count": 1
          },
          {
            "date": "2024-05-02",
            "count": 1
          },
          {
            "date": "2024-05-12",
            "count": 1
          },
          {
            "date": "2024-05-13",
            "count": 1
          },
          {
            "date": "2024-05-14",
            "count": 1
          },
          {
            "date": "2024-12-18",
            "count": 1
          },
          {
            "date": "2025-06-15",
            "count": 1
          },
          {
            "date": "2025-10-03",
            "count": 1
          },
          {
            "date": "2025-12-25",
            "count": 1
          },
          {
            "date": "2026-01-15",
            "count": 1
          },
          {
            "date": "2026-02-10",
            "count": 1
          },
          {
            "date": "2026-04-21",
            "count": 1
          },
          {
            "date": "2026-05-23",
            "count": 2
          },
          {
            "date": "2026-05-24",
            "count": 1
          },
          {
            "date": "2026-05-25",
            "count": 7
          },
          {
            "date": "2026-05-30",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 172,
        "total_stars": 172
      },
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 30160,
      "source_files_sampled": 80,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "Gemfile"
      ],
      "advisories": {
        "error": "No resolved dependencies carried a version and a supported ecosystem",
        "scope": "repository_graph",
        "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": 22,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "rubygems"
      ],
      "dependencies": [
        {
          "name": "libxml-ruby",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": null
        },
        {
          "name": "nokogiri",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": null
        },
        {
          "name": "oga",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 2.3"
        },
        {
          "name": "ox",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": null
        },
        {
          "name": "rexml",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": null
        },
        {
          "name": "minitest",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 6"
        },
        {
          "name": "minitest-mock",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 5.27"
        },
        {
          "name": "mutant-minitest",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 0.14.1"
        },
        {
          "name": "rake",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 13.3.1"
        },
        {
          "name": "rbs",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 4.0.0"
        },
        {
          "name": "rdoc",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 7.0.2"
        },
        {
          "name": "rdoc",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 7.0.2"
        },
        {
          "name": "rubocop",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 1.81.7"
        },
        {
          "name": "rubocop-minitest",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 0.36"
        },
        {
          "name": "rubocop-performance",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 1.26.1"
        },
        {
          "name": "rubocop-rake",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 0.7.1"
        },
        {
          "name": "simplecov",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 1"
        },
        {
          "name": "standard",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 1.52"
        },
        {
          "name": "standard-performance",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 1.9"
        },
        {
          "name": "steep",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 2"
        },
        {
          "name": "yard",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 0.9.38"
        },
        {
          "name": "yardstick",
          "manifest": "Gemfile",
          "ecosystem": "rubygems",
          "version_constraint": ">= 0.9.9"
        }
      ],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [
          {
            "name": "libxml-ruby",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "minitest",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "minitest-mock",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "mutant-minitest",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "nokogiri",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "oga",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "ox",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rake",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rbs",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rdoc",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rexml",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rubocop",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rubocop-minitest",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rubocop-performance",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "rubocop-rake",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "simplecov",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "standard",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "standard-performance",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "steep",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "yard",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "yardstick",
            "direct": true,
            "version": null,
            "ecosystem": "rubygems"
          },
          {
            "name": "bigdecimal",
            "direct": false,
            "version": null,
            "ecosystem": "rubygems"
          }
        ],
        "collected": true,
        "truncated": false,
        "total_count": 22,
        "direct_count": 21,
        "indirect_count": 1
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 33,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 34,
        "closed_unmerged_prs": 8
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "sferik",
          "commits": 401,
          "avatar_url": "https://avatars.githubusercontent.com/u/10308?v=4"
        },
        {
          "type": "User",
          "login": "ohler55",
          "commits": 16,
          "avatar_url": "https://avatars.githubusercontent.com/u/118856?v=4"
        },
        {
          "type": "User",
          "login": "phiggins",
          "commits": 6,
          "avatar_url": "https://avatars.githubusercontent.com/u/22962?v=4"
        },
        {
          "type": "User",
          "login": "joshk",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/8701?v=4"
        },
        {
          "type": "User",
          "login": "Earlopain",
          "commits": 3,
          "avatar_url": "https://avatars.githubusercontent.com/u/14981592?v=4"
        },
        {
          "type": "User",
          "login": "hakanensari",
          "commits": 3,
          "avatar_url": "https://avatars.githubusercontent.com/u/31693?v=4"
        },
        {
          "type": "User",
          "login": "koic",
          "commits": 3,
          "avatar_url": "https://avatars.githubusercontent.com/u/13203?v=4"
        },
        {
          "type": "User",
          "login": "koraktor",
          "commits": 3,
          "avatar_url": "https://avatars.githubusercontent.com/u/66112?v=4"
        },
        {
          "type": "User",
          "login": "ka8725",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/243846?v=4"
        },
        {
          "type": "User",
          "login": "fisherwebdev",
          "commits": 2,
          "avatar_url": "https://avatars.githubusercontent.com/u/169760?v=4"
        }
      ],
      "contributors_sampled": 20,
      "top_contributor_share": 0.879
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "benchmark.yml",
        "docs.yml",
        "linter.yml",
        "mutant.yml",
        "push.yml",
        "tests.yml",
        "typecheck.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [
        ".rubocop.yml"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 10,
            "reason": "project has 16 contributing companies or organizations",
            "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 2 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 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": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "391efee2f509a7afde833eee8315f0ec7f59f16b",
        "ran_at": "2026-07-22T01:58:58Z",
        "aggregate_score": 4.5,
        "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-13T21:09:52Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-04-22T22:23:11Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/sferik/multi_xml",
    "host": "github.com",
    "name": "multi_xml",
    "owner": "sferik"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 65,
      "inputs": {
        "security": 45,
        "vitality": 71,
        "community": 70,
        "governance": 68,
        "engineering": 68
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 71,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 62,
            "inputs": {
              "commits_last_year": 80,
              "human_commit_share": 1,
              "days_since_last_push": 8,
              "active_weeks_last_year": 9
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 8 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "9/52 weeks with commits",
                "points": 6.2,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 9
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "80 commits in the last year",
                "points": 17.1,
                "status": "partial",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 80
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "good",
            "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": 84,
            "inputs": {
              "releases_count": 1,
              "latest_release_tag": "v0.9.1",
              "releases_from_tags": false,
              "days_since_latest_release": 78,
              "mean_days_between_releases": null
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "1 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 78 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 78
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "cadence unknown (single release)",
                "points": 12.6,
                "status": "partial",
                "details": [
                  {
                    "code": "release_cadence_unknown",
                    "params": {}
                  }
                ],
                "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": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 8,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 8 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "good",
        "name": "Community & Adoption",
        "value": 70,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "moderate",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 52,
            "inputs": {
              "forks": 38,
              "stars": 172,
              "watchers": 4,
              "growth_state": "organic",
              "growth_factor_pct": 100
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "172 stars",
                "points": 36.2,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 172
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "38 forks",
                "points": 13.1,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 38
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "4 watchers",
                "points": 2.7,
                "status": "partial",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 70,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "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": 18,
                "status": "met",
                "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
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "excellent",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "packages": [
                "multi_xml"
              ],
              "dependents": null,
              "ecosystems": "rubygems",
              "total_downloads": 583709139,
              "monthly_downloads": null
            },
            "components": [
              {
                "key": "total_downloads",
                "name": "Total downloads",
                "detail": "583,709,139 downloads all-time across rubygems",
                "points": 80,
                "status": "met",
                "details": [
                  {
                    "code": "downloads_total",
                    "params": {
                      "count": 583709139,
                      "ecosystems": "rubygems"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 68,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "at_risk",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 35,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 20,
              "top_contributor_share": 0.879
            },
            "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 88% of commits",
                "points": 2.7,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 88
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "20 contributors",
                "points": 13.5,
                "status": "met",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 16 contributing companies or organizations",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "good",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 78,
            "inputs": {
              "merged_prs": 33,
              "open_issues": 0,
              "closed_issues": 34,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 8
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "100% of issues closed",
                "points": 46.8,
                "status": "met",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "33/41 decided PRs merged",
                "points": 30.8,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 33,
                      "decided": 41
                    }
                  }
                ],
                "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": "good",
            "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": 74,
            "inputs": {
              "followers": 2649,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "sferik",
              "public_repos": 216,
              "account_age_days": 6642
            },
            "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": "2,649 followers of sferik",
                "points": 24.6,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 2649,
                      "login": "sferik"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "216 public repos, account ~18 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 216
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 18
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "multi_xml"
              ],
              "ecosystems": "rubygems",
              "any_deprecated": false,
              "min_days_since_publish": 78
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on rubygems",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "rubygems"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 78 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 78
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "29 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 29
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "moderate",
        "name": "Engineering Quality",
        "value": 68,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 80,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "7 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 7
                    }
                  }
                ],
                "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": ".rubocop.yml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".rubocop.yml"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": false,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 45,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 45,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 4.5
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 16 contributing companies or organizations",
                "points": 2.5,
                "status": "met",
                "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 2 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "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": 20
            },
            "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": "at_risk",
        "name": "AI Readiness",
        "value": 32,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "critical",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 18,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.34,
              "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": "34 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 18.1,
                "status": "partial",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 34,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "at_risk",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 33,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": ".rubocop.yml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": ".rubocop.yml"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "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": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "Ruby",
              "largest_source_bytes": 30160,
              "source_files_sampled": 80,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Ruby without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "Ruby"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/80 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 80,
                      "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": [
    "No resolved dependencies carried a version and a supported ecosystem"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-22T01:59:09.869304Z",
  "schema_version": "0.26.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/s/sferik/multi_xml.svg",
  "full_name": "sferik/multi_xml",
  "license_state": "standard",
  "license_spdx": "MIT"
}

评分是信号,而非担保。 评分反映的是 GitHub 上公开可见的实践——不是代码审计,也不是安全保证。

缺失数据将被剔除并重新归一化权重,绝不按零分计。方法论已版本化并公开:指标 v1.13.0、模式 v0.26.0—— 完整方法论 · 指标知识库.

单项结果在整体记录中的位置: 汇总统计RubyGems.