Public record
Software health reportschema 0.26.0 · metrics 1.13.0 · 2026-07-22 01:59 UTC

sferik / multi_xml

A generic swappable back-end for XML parsing

RubyMIT★ 172 stars⑂ 38 forkssince Oct 2010View on GitHub ↗

sferik/multi_xml holds a health index of 65 out of 100, placing it in the Moderate band. It scores highest on Vitality (71/100) and lowest on AI Readiness (32/100). It was last updated 8 days ago. A single contributor accounts for most of its recent work.

65
overall / 100
Moderate

Software health index

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

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

Score profile

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

Ownership

Erik BerlinPersonal account
2,649 followers216 public repossince May 2008

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

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publish
RubyGemsmulti_xml0.9.1-2978 days ago

Metrics by category

Vitality

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

71Good · 22% of overall
How it's scored
28.8/36Push recency — last push 8 days ago
6.2/36Commit cadence — 9/52 weeks with commits
17.1/18Commit volume — 80 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year80
human_commit_share1
days_since_last_push8
active_weeks_last_year9
How it's scored
27/27Ships releases — 1 releases published
36/36Release recency — latest release 78 days ago
12.6/27Release cadence — cadence unknown (single release)
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count1
latest_release_tagv0.9.1
releases_from_tagsno
days_since_latest_release78
mean_days_between_releases
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

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

70Good · 18% of overall
How it's scored
36.2/60Stars — 172 stars
13.1/25Forks — 38 forks
2.7/15Watchers — 4 watchers
Inputs used
forks38
stars172
watchers4
growth_stateorganic
growth_factor_pct100
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (MIT)
18/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
0/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingyes
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno
How it's scored
80/80Total downloads — 583,709,139 downloads all-time across rubygems
0/20Registry dependents — not reported by this ecosystem
Inputs used
packagesmulti_xml
dependents
ecosystemsrubygems
total_downloads583,709,139
monthly_downloads
Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.

Sustainability & Governance

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

68Moderate · 24% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
2.7/22.5Commit distribution — top contributor authored 88% of commits
13.5/13.5Contributor breadth — 20 contributors
10/10OpenSSF Scorecard: Contributors — project has 16 contributing companies or organizations
Inputs used
bus_factor1
contributors_sampled20
top_contributor_share0.879
How it's scored
46.8/46.8Issue resolution — 100% of issues closed
30.8/38.3PR acceptance — 33/41 decided PRs merged
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Inputs used
merged_prs33
open_issues0
closed_issues34
issue_closed_ratio1
closed_unmerged_prs8
How it's scored
10/30Ownership backing — personal (user) account
0/20Verified domain — not applicable to user accounts
24.6/25Owner reach — 2,649 followers of sferik
25/25Track record — 216 public repos, account ~18 yr old
Inputs used
followers2,649
owner_typeUser
is_verified
owner_loginsferik
public_repos216
account_age_days6,642
Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.
How it's scored
25/25Published & resolvable — 1 package(s) on rubygems
35/35Publish recency — latest publish 78 days ago
20/20Version history — 29 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagesmulti_xml
ecosystemsrubygems
any_deprecatedno
min_days_since_publish78

Engineering Quality

Are baseline engineering and documentation practices in place?

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

Documentation

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

Security

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

45At risk · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — no data
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
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.5License — 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 — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4.5
Excluded from scoring (no data or not applicable): ci_tests, signed_releases. Remaining weights renormalized.

AI Readiness

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

32At risk · 0% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
18.1/40Legible commit history — 34 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.34
agent_instruction_files
agent_instruction_max_bytes
How it's scored
0/18One-command bootstrap
22/22Automated tests
11/11Lint / format config — .rubocop.yml
0/11Static type checking
0/10Reproducible environment
0/10Demonstrated agent practice — no agent-authored commits among the last 100
0/8Automated maintenance — no automated dependency updates observed
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfiles
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configyes
typecheck_configs
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
How it's scored
0/45Type-checkable code — Ruby without a type-check config
55/55Manageable file sizes — 0/80 source files over 60KB
Inputs used
primary_languageRuby
largest_source_bytes30,160
source_files_sampled80
oversized_source_files0

Key facts

172GitHub stars
20contributors
80commits, last 12 months
8days since last push
1releases
1bus factor
0open issues
RubyGemspackage ecosystems

Data collection warnings

  • No resolved dependencies carried a version and a supported ecosystem

More detail

Star and fork history 172 ★ / 38 ⇿
172Stars
38Forks
1Releases

When each star and fork was added, collected from GitHub and bucketed by day. Cumulative growth sits directly above the daily additions it is made of, so the two read against each other: steady organic accretion looks nothing like an abrupt, short-lived burst. Where that difference is measurable, it is reported as growth authenticity.

0408012016020017237542010-102018-072026-05
Major 0Minor 0Patch 1

Each point covers 15 days.

OpenSSF Scorecard 4.5 / 10
4.5aggregate

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

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/aCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
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
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Direct dependencies 22
RegistryPackageVersion constraintManifest
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
All dependencies 22

Full resolved dependency set from the GitHub dependency graph: 21 direct and 1 indirect (transitive) packages. The transitive closure is complete when the repository commits a lockfile.

RegistryPackageVersionRelation
RubyGemslibxml-rubydirect
RubyGemsminitestdirect
RubyGemsminitest-mockdirect
RubyGemsmutant-minitestdirect
RubyGemsnokogiridirect
RubyGemsogadirect
RubyGemsoxdirect
RubyGemsrakedirect
RubyGemsrbsdirect
RubyGemsrdocdirect
RubyGemsrexmldirect
RubyGemsrubocopdirect
RubyGemsrubocop-minitestdirect
RubyGemsrubocop-performancedirect
RubyGemsrubocop-rakedirect
RubyGemssimplecovdirect
RubyGemsstandarddirect
RubyGemsstandard-performancedirect
RubyGemssteepdirect
RubyGemsyarddirect
RubyGemsyardstickdirect
RubyGemsbigdecimalindirect
Dependency advisories not assessed

Advisory matching could not run for this report: No resolved dependencies carried a version and a supported ecosystem

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

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

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

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