公开记录
软件健康报告模式 0.27.0 · 指标 1.13.0 · 2026-07-31 12:17 UTC

jhuntwork / mere

A mirror repository for the mere package manager

ZigMIT★ 5 星标⑂ 0 复刻始于 2019年8月在 GitHub 上查看 ↗

jhuntwork/mere 的健康指数为 100 分中的 50 分,处于「中等」区间。 其得分最高的类别是Vitality(75/100),最低的是Community & Adoption(29/100)。 最近一次更新在 10 天前。 近期的大部分工作由 1 位贡献者完成。

50
总分 / 100
中等

软件健康指数

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

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

评分画像

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

所有权

Jeremy Huntwork个人账户
42 关注者23 个公开仓库始于 2010年4月

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

按类别列示的指标

活力

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

75良好 · 占总体的 22%
评分方式
28.8/36推送新近度 — 最近一次推送于 10 天前
9.7/36提交节奏 — 52 周中有 14 周有提交
18/18提交量 — 最近一年 272 次提交
10/10OpenSSF Scorecard:Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
所用输入
commits_last_year272
human_commit_share1
days_since_last_push10
active_weeks_last_year14

发布纪律

88优秀
评分方式
16.2/27有发布版本 — 32 个版本标签(无 GitHub 发布版本)
36/36发布时效 — 最近一次发布版本于 10 天前
27/27发布节奏 — 约每 11.9 天发布一次
0/10OpenSSF Scorecard:Signed-Releases — 无数据
所用输入
releases_count32
latest_release_tagv0.16.1
releases_from_tags
days_since_latest_release10
mean_days_between_releases11.9
已排除计分(无数据或不适用):OpenSSF Scorecard:Signed-Releases。 其余权重已重新归一化。

社区与采用

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

29危急 · 占总体的 18%
评分方式
9.8/60星标 — 5 个星标
0/25复刻 — 0 个复刻
0/15关注者 — 2 位关注者
所用输入
forks0
stars5
watchers2
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history

社区健康

50中等
评分方式
22.5/22.5README
22.5/22.5许可证 — 可识别的许可证(MIT)
0/18CONTRIBUTING 指南
0/13.5行为准则
0/7.2议题模板
0/6.3PR 模板
所用输入
has_readme
has_license
has_contributing
has_issue_template
has_code_of_conduct
has_pull_request_template

可持续性与治理

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

48存在风险 · 占总体的 24%
评分方式
9/54巴士系数 — 1 位贡献者贡献了半数提交
0/22.5提交分布 — 头号贡献者编写了 100% 的提交
1.4/13.5贡献者广度 — 1 位贡献者
0/10OpenSSF Scorecard:Contributors — project has 0 contributing companies or organizations -- score normalized to 0
所用输入
bus_factor1
contributors_sampled1
top_contributor_share1
评分方式
46.8/46.8议题解决 — 100% 的议题已关闭
38.2/38.3PR 接受 — 已裁定的 PR 中 15/15 已合并
0/15OpenSSF Scorecard:Code-Review — Found 0/30 approved changesets -- score normalized to 0
所用输入
merged_prs15
open_issues0
closed_issues1
issue_closed_ratio1
closed_unmerged_prs0
评分方式
10/30所有权背书 — 个人(用户)账户
0/20已验证域名 — 不适用于个人账户
11.7/25所有者影响力 — jhuntwork 有 42 位关注者
22.1/25既往记录 — 23 个公开仓库,账户约 16 年
所用输入
followers42
owner_typeUser
is_verified
owner_loginjhuntwork
public_repos23
account_age_days5,957
已排除计分(无数据或不适用):已验证域名。 其余权重已重新归一化。

工程质量

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

54中等 · 占总体的 20%

工程实践

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

文档

90优秀
评分方式
30/30README
25/25文档目录
15/15文档 / 主页站点 — https://codeberg.org/merelinux/mere
10/10仓库描述
0/10主题标签
10/10Wiki
所用输入
topics
has_wiki
homepagehttps://codeberg.org/merelinux/mere
has_readme
has_docs_dir
has_description

安全

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

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

安全态势

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

AI 就绪度

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

35存在风险 · 占总体的 0%
评分方式
0/45代理指令 — 没有 CLAUDE.md / AGENTS.md / 编辑器规则
0/15机器可读文档(llms.txt)
40/40可读的提交历史 — 100 次人类提交中有 99 次说明了意图(结构化标题或解释性正文)
所用输入
has_llms_txt
legible_history_share0.99
agent_instruction_files
agent_instruction_max_bytes
评分方式
0/18一条命令的引导启动
22/22自动化测试
0/11Lint / 格式化配置
0/11静态类型检查
0/10可复现环境
0/10已体现的代理实践 — 最近 100 次提交中没有代理编写的提交
0/8自动化维护 — 未观察到自动依赖更新
0/10OpenSSF Scorecard:Pinned-Dependencies — 无数据
所用输入
has_nix
has_tests
lockfiles
has_dockerfile
typed_language
bootstrap_files
has_devcontainer
has_linter_config
typecheck_configs
agent_commit_share0
toolchain_manifests
dependency_bot_commit_share0
已排除计分(无数据或不适用):OpenSSF Scorecard:Pinned-Dependencies。 其余权重已重新归一化。
评分方式
0/45可类型检查的代码 — Zig,未配置类型检查
55/55可控的文件大小 — 采样的 1 个源文件中有 0 个超过 60KB
所用输入
primary_languageZig
largest_source_bytes441
source_files_sampled1
oversized_source_files0

关键数据

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

数据采集警告

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token

更多细节

OpenSSF Scorecard 3.7 / 10
3.7综合

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

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
不适用CI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
不适用Dangerous-Workflowno workflows found
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
不适用Packagingpackaging workflow not detected
不适用Pinned-Dependenciesno dependencies found
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
不适用Signed-Releasesno releases found
不适用Token-PermissionsNo tokens found
10Vulnerabilities0 existing vulnerabilities detected
全部依赖 0

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

注册表软件包版本关系
依赖安全公告 未评估

本报告未能完成公告比对:No resolved dependencies to assess

原始 JSON 报告 机器可读
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 1000,
      "has_wiki": true,
      "homepage": "https://codeberg.org/merelinux/mere",
      "languages": {
        "Zig": 2931532,
        "Shell": 1719,
        "Python": 441,
        "Vim Script": 4179
      },
      "pushed_at": "2026-07-21T02:04:13Z",
      "created_at": "2019-08-23T14:17:58Z",
      "owner_type": "User",
      "updated_at": "2026-07-21T01:53:45Z",
      "description": "A mirror repository for the mere package manager",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Zig",
      "significant_languages": [
        "Zig"
      ]
    },
    "owner": {
      "blog": "https://merelinux.org/",
      "name": "Jeremy Huntwork",
      "type": "User",
      "login": "jhuntwork",
      "company": null,
      "location": "New York, NY",
      "followers": 42,
      "avatar_url": "https://avatars.githubusercontent.com/u/239626?v=4",
      "created_at": "2010-04-08T15:38:11Z",
      "is_verified": null,
      "public_repos": 23,
      "account_age_days": 5957
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.16.1",
          "kind": "patch",
          "published_at": "2026-07-21T01:53:28Z"
        },
        {
          "tag": "v0.16.0",
          "kind": "minor",
          "published_at": "2026-07-21T00:57:08Z"
        },
        {
          "tag": "v0.15.2",
          "kind": "patch",
          "published_at": "2026-07-18T18:15:34Z"
        },
        {
          "tag": "v0.15.1",
          "kind": "patch",
          "published_at": "2026-07-18T16:10:39Z"
        },
        {
          "tag": "v0.15.0",
          "kind": "minor",
          "published_at": "2026-07-18T01:07:42Z"
        },
        {
          "tag": "v0.14.0",
          "kind": "minor",
          "published_at": "2026-06-04T17:07:24Z"
        },
        {
          "tag": "v0.13.2",
          "kind": "patch",
          "published_at": "2026-06-01T00:59:01Z"
        },
        {
          "tag": "v0.13.1",
          "kind": "patch",
          "published_at": "2026-05-28T14:36:43Z"
        },
        {
          "tag": "v0.13.0",
          "kind": "minor",
          "published_at": "2026-05-20T13:47:43Z"
        },
        {
          "tag": "v0.12.2",
          "kind": "patch",
          "published_at": "2026-04-04T22:38:04Z"
        },
        {
          "tag": "v0.12.1",
          "kind": "patch",
          "published_at": "2026-04-03T22:47:41Z"
        },
        {
          "tag": "v0.12.0",
          "kind": "minor",
          "published_at": "2026-04-03T21:44:43Z"
        },
        {
          "tag": "v0.11.1",
          "kind": "patch",
          "published_at": "2026-04-03T18:13:09Z"
        },
        {
          "tag": "v0.11.0",
          "kind": "minor",
          "published_at": "2026-04-02T13:33:43Z"
        },
        {
          "tag": "v0.10.4",
          "kind": "patch",
          "published_at": "2026-04-01T16:52:10Z"
        },
        {
          "tag": "v0.10.3",
          "kind": "patch",
          "published_at": "2026-03-31T20:05:26Z"
        },
        {
          "tag": "v0.10.2",
          "kind": "patch",
          "published_at": "2026-03-31T02:13:54Z"
        },
        {
          "tag": "v0.10.1",
          "kind": "patch",
          "published_at": "2026-03-30T22:15:20Z"
        },
        {
          "tag": "v0.10.0",
          "kind": "minor",
          "published_at": "2026-03-30T20:02:54Z"
        },
        {
          "tag": "v0.9.7",
          "kind": "patch",
          "published_at": "2026-03-29T21:51:08Z"
        },
        {
          "tag": "v0.9.5",
          "kind": "patch",
          "published_at": "2026-03-29T21:20:46Z"
        },
        {
          "tag": "v0.9.4",
          "kind": "patch",
          "published_at": "2026-03-29T20:43:20Z"
        },
        {
          "tag": "v0.9.2",
          "kind": "patch",
          "published_at": "2026-03-29T20:10:42Z"
        },
        {
          "tag": "v0.9.0",
          "kind": "minor",
          "published_at": "2026-03-26T02:50:55Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2026-03-25T04:53:51Z"
        },
        {
          "tag": "v0.7.2",
          "kind": "patch",
          "published_at": "2026-03-25T04:23:58Z"
        },
        {
          "tag": "v0.7.1",
          "kind": "patch",
          "published_at": "2026-03-25T02:16:48Z"
        },
        {
          "tag": "v0.7.0",
          "kind": "minor",
          "published_at": "2026-03-24T03:48:10Z"
        },
        {
          "tag": "v0.6.5",
          "kind": "patch",
          "published_at": "2026-03-23T12:38:57Z"
        },
        {
          "tag": "v0.6.4",
          "kind": "patch",
          "published_at": "2026-03-22T01:16:16Z"
        },
        {
          "tag": "v0.6.3",
          "kind": "patch",
          "published_at": "2026-03-22T00:35:11Z"
        },
        {
          "tag": "v0.6.2",
          "kind": "patch",
          "published_at": "2026-03-21T21:46:50Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "83bd82c434c2b7bcc854f6dc750206d970427025",
          "body": null,
          "is_bot": false,
          "headline": "release: v0.16.1",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-21T01:53:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7e24d3858606250ce2b4754de08fcdf2deff433b",
          "body": "…25) from fix/logical-store-symlinks into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/125",
          "is_bot": false,
          "headline": "Merge pull request 'Use logical store paths for profile symlinks' (#1…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-21T01:51:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "903be35af5b546236a37017c6bee6571211e6887",
          "body": "Profile symlinks embedded the physical --root prefix\n({root}/mere/store/...), which dangles inside the build\nnamespace: it bind-mounts {root}/mere onto /mere, so the store\nis only reachable at /mere/store/..., not at the host path.\nexecve(\"/bin/sh\") then failed with ENOENT before any output,\nproduci\n[…]\n/\") logical and physical are\nidentical.\n\nVerified: full test suite passes and mere dev build --root now\nruns the build script (fails only on an unrelated utmpx.h/musl\nrecipe issue, out of scope here).",
          "is_bot": false,
          "headline": "Use logical store paths for profile symlinks",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-21T01:47:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6262108214248749ecbe2117698114cf82b209f3",
          "body": "Bump version for automated release.\n\nUpdates README.md install-instructions download URLs to match.",
          "is_bot": false,
          "headline": "release: v0.16.0",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-21T00:57:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "92d0993768de30720d621cf61ce1536c67caf14f",
          "body": "…namespace' (#124) from fix/namespace-mere-root-bind into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/124",
          "is_bot": false,
          "headline": "Merge pull request 'Use opts.mere_root instead of hardcoded /mere in …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-21T00:55:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "954d6bc8f7cbb6e400e0f67d9bdb413949be8eef",
          "body": "…23) from metadata-property-passthrough into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/123",
          "is_bot": false,
          "headline": "Merge pull request 'Pass recipe metadata through release publish' (#1…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-21T00:52:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1f2c03b3ab12f1da6da45cdbb882e4ccb5b26b1",
          "body": "buildSyntheticRoot bind-mounted the host's /mere unconditionally,\nwhich breaks when --root points to a different location. Profile\nsymlinks resolve against the store at the specified root, but the\nnamespace was mounting the wrong /mere over them.\n\nAdd mere_root field to EnvOptions (defaults to \"/mere\" for backward\ncompat). Both shell.zig and build_orchestrator.zig now pass\n{root}/mere derived from the --root flag.",
          "is_bot": false,
          "headline": "Use opts.mere_root instead of hardcoded /mere in namespace",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-21T00:50:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9e25a248a30aecf94a963f8c80348e8c311c4a08",
          "body": "release publish was dropping the properties column when copying packages\nfrom the dev repo to the release output. Import correctly built and\nstored the metadata JSON (description, url, licenses, source_urls) from\nmeta.kdl, but publish passed null to insertPackageTransaction — so the\nrelease repo.db \n[…]\n→ dev repo properties column → release repo.db properties\ncolumn.\n\nVerified end-to-end via swamp mere-pkgd-e2e workflow: build → publish →\nquery properties column confirms all metadata fields present.",
          "is_bot": false,
          "headline": "Pass recipe metadata through release publish",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-19T17:15:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5ae686ef376b77dde7646aa79b555a225087a77a",
          "body": "Reviewed-on: https://codeberg.org/merelinux/mere/pulls/122",
          "is_bot": false,
          "headline": "Merge pull request 'release: v0.15.2' (#122) from v0.15.2 into main",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-18T18:15:34Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6d6a52d67047ce657d571fcf0928fb53c882def8",
          "body": "Bump version for automated release.\n\nUpdates README.md install-instructions download URLs to match.",
          "is_bot": false,
          "headline": "release: v0.15.2",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T18:11:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e405354b28bb8e33d79d83ecec23ffd15a3f03dc",
          "body": "…from import-log-package-details into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/121",
          "is_bot": false,
          "headline": "Merge pull request 'Log package details on successful import' (#121) …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-18T18:10:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f312cb79a5222b2072c6585a821e9d805c37de88",
          "body": "Emit an info-level log line for each package as it is imported,\nshowing name, version, release, and architecture. Previously only\nthe total count was reported at the CLI level, with no per-package\nvisibility during the import process.",
          "is_bot": false,
          "headline": "Log package details on successful import",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T18:06:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4cf070582f6cba42150573e13fd5d707a6e143f8",
          "body": "…20) from fix/prune-by-version-not-id into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/120",
          "is_bot": false,
          "headline": "Merge pull request 'Use version comparison for retention pruning' (#1…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-18T18:03:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7fe4aa04f5462d0e7e2e31352ed0f00d12f41132",
          "body": "pruneOldVersions previously used ORDER BY id DESC to determine\nwhich packages to keep, meaning insertion order decided retention.\nIf packages were imported out of order, the newest version could\nbe pruned while an older one was kept.\n\nRewrite to fetch all versions for a (name, arch) pair, sort using\n[…]\ne resolver and\ngetLatestPackagesByNameArch), and prune everything below the top\nkeep_count.\n\nAlso fix collectPruneCandidates in release.zig to use the same\nversion-comparison approach for consistency.",
          "is_bot": false,
          "headline": "Use version comparison for retention pruning",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T17:59:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c60ae9e679a12ee0cd98da611508d02d68946d06",
          "body": "Bump version for automated release.\n\nAlso, switch CI runners to docker-aarch64 where possible for faster\nbuilds. Extract test step into its own job on aarch64, gating both\nrelease builds. Only release-x86_64 stays on docker since it must\nproduce the x86_64 binary.\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/119",
          "is_bot": false,
          "headline": "v0.15.1 (#119)",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T16:10:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1d32ad0e095dceecc7232e87685ed3e3734bc139",
          "body": "…ilding' (#118) from release-publish-merge-semantics into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/118",
          "is_bot": false,
          "headline": "Merge pull request 'Merge new packages into output DB instead of rebu…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-18T14:58:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "17b6c6b6c0ba018108a87211d0af76d403ed246a",
          "body": "release publish no longer clears the output repo.db on every\ninvocation. Instead it preserves the existing published state and\nmerges in new packages from the dev repo, skipping duplicates.\n\nRetention (keep 3 per name+arch) is applied across the full output\nDB after insertion. Orphaned archive files\n[…]\nows.\n\nThis makes the output directory the single source of truth for what\nis currently published, allowing pkgd to treat the dev repo as an\nephemeral staging area rather than a persistent accumulator.",
          "is_bot": false,
          "headline": "Merge new packages into output DB instead of rebuilding",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T14:46:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bba6a369a247b1a03593128e6f9de7344d57bc6b",
          "body": "Bump version for automated release.\n\nUpdates README.md's install-instructions download URLs to match.\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/117",
          "is_bot": false,
          "headline": "release: v0.15.0 (#117)",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-18T01:07:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "191acea802e09e26dd453f5fa80d0e98ac1db115",
          "body": "…ease-publish into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/116",
          "is_bot": false,
          "headline": "Merge pull request 'Add mere release publish command' (#116) from rel…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-18T00:37:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "05820557275f84ea20f9da72daa239a4b122f9e6",
          "body": "Implements specification 9.9 (Release Publication Requirements): a\nnew mere release publish --dev-repo --output [--key] command that\nbuilds a signed public release (repo.db, repo.db.sig, packages/) from\na dev repository as the sole source of truth, applying keep-count\nretention and failing loudly be\n[…]\nput are explicit, caller-supplied paths\nrather than workstation conventions, so this can run against\narbitrary directories in a server context (see the pkgd service,\nwhich shells out to this command).",
          "is_bot": false,
          "headline": "Add mere release publish command",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-17T21:16:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "27a727bbd32c94993ebe3eb2d881b6e2e10f7e5e",
          "body": "… errors to FileSystem' (#115) from sign-verifysignature-error-mapping into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/115",
          "is_bot": false,
          "headline": "Merge pull request 'Stop collapsing verifySignature's public-key-load…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-15T21:41:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "982e7893bd056f8be66228be29496547498e4feb",
          "body": "verifySignature caught every PublicKey.loadFromFile error and mapped\nit to a generic SignError.FileSystem, even though loadFromFile already\nreturns the fully-specific SignError itself - same pattern as\ngetFileHash. Propagate the error directly instead of remapping it.",
          "is_bot": false,
          "headline": "Stop collapsing verifySignature's public-key-load errors to FileSystem",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-13T18:23:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f99287aab69f2fa0b13e1481a3c407582c63f70e",
          "body": "…sage' (#114) from extract-libarchive-message-strings into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/114",
          "is_bot": false,
          "headline": "Merge pull request 'Match libarchive's actual absolute-path error mes…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-13T17:27:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "07271d79f76b95bf109699469d093794c28cd3be",
          "body": "classifyLibarchiveMessage checked for \"Absolute path\" and \"Bad path\",\nneither of which libarchive ever actually produces (checked against\nthe vendored 3.8.2 source: archive_write_disk_posix.c's\nARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS branch emits \"Path is\nabsolute\" via fsobj_error's \"%s%s\" concatenat\n[…]\nr anywhere in libarchive). Currently harmless since mere never\nsets that flag, but would silently fail to classify the message if\nit's added later for defense-in-depth. Fixed to match the real string.",
          "is_bot": false,
          "headline": "Match libarchive's actual absolute-path error message",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-13T16:26:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9a9121c22ffd3f93d77348878761a4b32f5a4934",
          "body": "…air' (#113) from repo-history-atomic-commit into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/113",
          "is_bot": false,
          "headline": "Merge pull request 'Publish repo.db and repo.db.sig atomically as a p…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-13T00:22:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4244eb2d49253b15d87e17c8bc30f63614d08ac6",
          "body": "Staged.commit published the staged db and sig via two independent,\nnon-atomic copyFile calls. A crash between them left a mismatched\npair on disk - fails closed (every subsequent verify rejects the\nmismatch) but leaves the repo unusable until manually re-signed.\n\nAdded replaceLiveDbAndSigWithRollback, mirroring repocache.zig's\nexisting replaceCachedDbAndSigWithRollback: back up whatever is\ncurrently live, rename the new db and sig into place, and roll back\nto the backups on any partial failure.",
          "is_bot": false,
          "headline": "Publish repo.db and repo.db.sig atomically as a pair",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-12T23:34:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a11408b36dbb3c2599ffa5384700a408040372a0",
          "body": "…mentInternal' (#112) from kdl-parse-node-double-free into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/112",
          "is_bot": false,
          "headline": "Merge pull request 'Fix double-free of a KDL node on OOM in parseDocu…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-12T22:44:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "784df46d21238be773b0c4edd23f1a47bbb38613",
          "body": "KDL_EVENT_START_NODE registered errdefer new_node.deinit() right after\nNode.init, but never cancelled it once new_node was successfully\nappended into nodes or parent.children. A later failure in the same\nbranch (node_stack.append OOMing) fired that errdefer and\nparseDocumentInternal's own nodes-clea\n[…]\nne node.\n\nFixed with the same appended-flag pattern already used elsewhere in\nthis codebase for this exact ownership-transfer shape: the errdefer\nonly fires if the node was never handed off to a list.",
          "is_bot": false,
          "headline": "Fix double-free of a KDL node on OOM in parseDocumentInternal",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-12T22:08:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ad9266aabdbf1e032e47aae32caef4faa82a85a1",
          "body": "…) from build-cache-locking into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/111",
          "is_bot": false,
          "headline": "Merge pull request 'Add locking around the on-disk build cache' (#111…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-12T19:37:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0ec5033d61ac99453492337301a2b40d8dd6bdd5",
          "body": "gc() and the various storeXForKey() functions mutated the same\non-disk trees (keys/, artifacts/) with zero coordination, unlike the\npackage store's Context.acquireStoreLock(). Two concrete races:\ngc() could delete a just-persisted artifact before its key record was\nwritten (self-healing - the next b\n[…]\nePackageArchiveForKey().\n\nclear() now skips deleting its own .lock file while iterating\ncache_root's entries, since that file is cache infrastructure the lock\njust created, not cache content to clear.",
          "is_bot": false,
          "headline": "Add locking around the on-disk build cache",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-12T17:27:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "af370e6803dbf815eaeb15927b95c368cd38bac8",
          "body": "…rBuild; fix leaks and a double-free in repo_sources.zig' (#110) from repo-caches-diagnostic-context-guard into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/110",
          "is_bot": false,
          "headline": "Merge pull request 'Preserve diagnostic context in createRepoCachesFo…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T22:49:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be29782f3f05a90e983b07357d57cd24e3f5584a",
          "body": "…d a double-free in repo_sources.zig\n\ncreateRepoCachesForBuild unconditionally overwrote whatever diagnostic\ncontext repo_sources.createCaches already set (e.g. a specific repo\nname and \"failed to initialize repository cache\") with a generic\n\"config\"/\"failed to create repo caches from config\", incon\n[…]\nror after the second\n  defer double-freed it.\n\nAlso fixed test_helpers.createTestRepoConfig, which referenced a\nnonexistent .ctx field on RepoConfig - needed for the new\ncreateRepoCachesForBuild test.",
          "is_bot": false,
          "headline": "Preserve diagnostic context in createRepoCachesForBuild; fix leaks an…",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T21:33:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "df5599acd1472d7aa2242db115530ecfa418c41e",
          "body": "…e failure diagnostics' (#109) from namespace-phase-error-detail into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/109",
          "is_bot": false,
          "headline": "Merge pull request 'Include the specific namespace error name in phas…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T17:33:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a1b193cf902df2fe38d20e1f34d2f842b26a5590",
          "body": "runOneNamespacePhase set a static \"failed to fork and enter env\" message\nregardless of which of namespace.EnvError's ~20 variants (uid-map\nfailure, overlayfs unavailable, disabled user namespaces, mount\nfailures, etc.) actually occurred, with no way for an operator to tell\nthem apart from the message alone. Interpolate @errorName(err) into the\ndiagnostic details via setDiagnosticContextFmt instead of the static\nstring.",
          "is_bot": false,
          "headline": "Include the specific namespace error name in phase failure diagnostics",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T17:04:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9c5dae61988390acbbb348b07d660b9ae07a1bd2",
          "body": "…ileSystem error' (#108) from sign-getfilehash-error-mapping into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/108",
          "is_bot": false,
          "headline": "Merge pull request 'Stop collapsing getFileHash errors to a generic F…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T16:41:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "05a82d3bbb9e4c89eb966a5b3a6b588482f8e06e",
          "body": "getFileHash's catch on hash.calculateFileHash mapped every possible\nerror - including OutOfMemory and PermissionDenied - to a generic\nSignError.FileSystem, discarding the specific error calculateFileHash\nhad already computed. This backs defaultSigner (used when signing repo\ndatabases), and repositor\n[…]\n is to\njust propagate the error directly instead of remapping it - the\nAccessDenied/EndOfStream/Unexpected branches being collapsed over were\nalready dead code, since HashError can never produce them.",
          "is_bot": false,
          "headline": "Stop collapsing getFileHash errors to a generic FileSystem error",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T16:10:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e1ec2dbaa8845ca537e2693b1860fdc3c6dd3b9e",
          "body": "…on-bomb-sized payload' (#107) from extract-decompression-bomb-limit into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/107",
          "is_bot": false,
          "headline": "Merge pull request 'Reject archive entries that declare a decompressi…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T15:50:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2b20b06468eb3657aaea43cf7ecb00970ea68014",
          "body": "extractWithLibarchive extracted every entry regardless of its declared\nuncompressed size, with no ceiling. The signed-install path is tempered\nby install.zig's whole-archive hash verification against trusted repo\nmetadata before extraction runs, but mere dev import (import.zig) and\nbuild-source unpa\n[…]\nhich\nrejects any entry whose archive_entry_size() exceeds a 4 GiB ceiling,\nwith a diagnostic message naming the entry and stating the declared\nsize and the limit rather than a generic \"invalid input\".",
          "is_bot": false,
          "headline": "Reject archive entries that declare a decompression-bomb-sized payload",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T15:24:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "375ab9de6a4a0164dc9ef829dfe2c6b579519ab6",
          "body": "…on restore' (#106) from build-cache-archive-restore-verify into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/106",
          "is_bot": false,
          "headline": "Merge pull request 'Re-verify cached archive hash before trusting it …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T13:52:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eef3869fc86a2f54253912da324761ac889af8cf",
          "body": "restorePackageArchiveForKey copied a cached package archive out of the\nbuild cache and returned its recorded archive_hash/signature verbatim,\nwith no check that those still matched the file's actual bytes. This\nwas asymmetric with storePackageArchiveForKey, which does verify before\ncaching. If the c\n[…]\nnow returns null instead of a mismatched hash/signature pair. Verified\nthe test fails without the fix (a non-null result is returned and\nleaked, since nothing frees a hit the caller wasn't expecting).",
          "is_bot": false,
          "headline": "Re-verify cached archive hash before trusting it on restore",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T13:32:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d8fb21e64aa77e97dff10514ea93e4054ab7eae4",
          "body": "…ntinueOnError' (#105) from split-staging-partial-cache into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/105",
          "is_bot": false,
          "headline": "Merge pull request 'Don't cache a partial split-stage result under Co…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T13:02:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "835fbb69c4ba6e3fd720ad9f355506938c45167d",
          "body": "Under FailurePolicy.ContinueOnError, stageSplitPackages can skip a\nsub-package that failed to stage and still return successfully (the\nerror is only recorded via split_staging_errors_encountered).\nrestoreOrStageSplitPackages unconditionally persisted that result to\nthe build cache regardless of the \n[…]\nkages, forcing one package to\nfail on the first call only. Verified the test fails without the fix\n(the second call restores the cached 1-package result instead of\nre-staging both) and passes with it.",
          "is_bot": false,
          "headline": "Don't cache a partial split-stage result under ContinueOnError",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T12:14:25Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ce618c0dd8e9154d35d27813187f08e237feaef6",
          "body": "…rtifact' (#104) from packaging-archive-hash-double-free into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/104",
          "is_bot": false,
          "headline": "Merge pull request 'Fix double-free of archive_hash in createPackageA…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T02:11:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7ca3c456436fb0dae08314bb180acc658de3e3f0",
          "body": "Two late-stage error branches in createPackageArtifact (the\nresult_package_name and result_signature dupe failures) manually freed\narchive_hash even though an errdefer registered earlier for archive_hash\nwas still armed, causing a double-free when OOM struck at either of\nthose two allocation sites.\n\n[…]\n to\ndeterministically reproduce the double-free. Verified the test fails\nagainst the unfixed code (GeneralPurposeAllocator reports the double\nfree at the exact two call sites) and passes with the fix.",
          "is_bot": false,
          "headline": "Fix double-free of archive_hash in createPackageArtifact",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T01:47:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "37bda82b7f1eb1768fcb771a3d9e289a26e8afe7",
          "body": "…y versions, not names' (#103) from build-cache-dependency-versions into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/103",
          "is_bot": false,
          "headline": "Merge pull request 'Key the build profile cache on resolved dependenc…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-10T01:18:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "06b2f123d3d78dadf7488355e648667141d49d84",
          "body": "computeProfileRealizeKey hashed a recipe's dependency *names* but never\nthe versions actually resolved for them. Dependency resolution is\ndynamic (an unpinned \"openssl\" resolves to whatever's newest in the\nsynced repo), so a repo sync that bumps a build-time dependency to a\nnew version was invisible\n[…]\nther than a second repo sync.\n\nAdded a regression test proving the key changes when a dependency's\nresolved content hash changes (and stays stable when it doesn't);\nconfirmed it fails without the fix.",
          "is_bot": false,
          "headline": "Key the build profile cache on resolved dependency versions, not names",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-10T00:34:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "67eeb7d8c1b88d0e9bc95be970cd654763035ae6",
          "body": "…fore touching the store' (#102) from pin-profile-store-lock into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/102",
          "is_bot": false,
          "headline": "Merge pull request 'Acquire the store lock in pin/profile commands be…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T22:50:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bab6852428ac8942e95b21cab4b853be01e21261",
          "body": "pin add, profile create --from, profile apply, and profile delete all\ncreate or reference store content or gc-roots without holding the\nstore lock that install/uninstall/generation commands already use. A\nconcurrent mere store clean's mark phase can miss a store path these\ncommands are about to refe\n[…]\nat pre-creates a valid, real store path (so\npin.create would legitimately succeed if reached) and blocks lock\nacquisition by pre-creating mere/.lock as a directory; confirmed it\nfails without the fix.",
          "is_bot": false,
          "headline": "Acquire the store lock in pin/profile commands before touching the store",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T21:48:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f41648ebc0426261b4c18939a9050aab079522a0",
          "body": "…tParser.parse' (#101) from parser-end-of-flags-separator into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/101",
          "is_bot": false,
          "headline": "Merge pull request 'Handle -- as an end-of-flags separator in Argumen…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T15:51:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "58fcca708cc4102da56d064d0b664872fe5690a2",
          "body": "ArgumentParser.parse had no handling for a literal \"--\" argument: it\nwould be matched by the startsWith(arg, \"--\") long-flag branch with an\nempty flag name and rejected as InvalidInput. cli.zig's one current\ncaller happens to strip \"--\" and everything after it before calling\nparse(), so this never s\n[…]\n\nAdded src/cli/parser.zig to build.zig's test_modules (same pattern as\ncommand.zig) since it had no test coverage at all; added tests\ncovering the new -- handling, confirmed they fail without the fix.",
          "is_bot": false,
          "headline": "Handle -- as an end-of-flags separator in ArgumentParser.parse",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T15:33:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ea14fe30807d4c1a65ef04976a0b0da9b193ccb3",
          "body": "…f curl+sha256sum' (#100) from vendor-deps-build-zig-zon into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/100",
          "is_bot": false,
          "headline": "Merge pull request 'Fetch vendored C deps via build.zig.zon instead o…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T15:15:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bc738e90301f36bdf538ec8440302f8f04a1b92d",
          "body": "Source acquisition for the vendored C libraries was a hand-rolled\nshell script (curl + sha256sum + tar) run at build time, participating\nin none of Zig's package infrastructure: no global cache dedup, no\n`--fetch` offline-build support, and a checksum format (sha256 hex)\nduplicating what Zig's own f\n[…]\n.zon's shape (only\n.version is ever read), which broke the moment .dependencies was\nadded. Letting Zig infer the type from the import directly makes it\nrobust against future build.zig.zon changes too.",
          "is_bot": false,
          "headline": "Fetch vendored C deps via build.zig.zon instead of curl+sha256sum",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T14:42:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "caec571e923c569303832e737d319e0d147d60a1",
          "body": "…cond disk read' (#99) from repo-db-verify-deserialize into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/99",
          "is_bot": false,
          "headline": "Merge pull request 'Open cached repo.db from verified bytes, not a se…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T03:18:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "651c505e6b00c91d29c9502d86749a40d552a3c7",
          "body": "RepoCache.ensureRepository hashed repo.db to verify its signature and\nthen let Repository.init reopen the same path fresh via sqlite3_open_v2,\nuntied to the hash that had just been checked. A file swapped in that\nwindow would make the verification meaningless for whatever sqlite\nactually read.\n\nsign\n[…]\nead the verified bytes through to this new path; the two\nother verifyWithTrustedFingerprints callers (repo_sources.zig,\nsyncLocal's download-then-rename flow) just needed to free the new\nreturn field.",
          "is_bot": false,
          "headline": "Open cached repo.db from verified bytes, not a second disk read",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T03:03:13Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c504a31a2fb34ef68683d8f2e12a51080299cef3",
          "body": "…re dev import' (#98) from import-verify-before-parse into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/98",
          "is_bot": false,
          "headline": "Merge pull request 'Verify manifest signature before parsing it in me…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T02:30:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fe17e830666c2b81a488d61d63cf03ad5a4f3a81",
          "body": "prepareVerifiedImport built a Package struct from the manifest before\nchecking its signature at all: readManifestAndCreatePackage decoded\nand used manifest.v1 first, and verifyManifestSignatureAndGetSigner\nonly checked the signature afterward against a separate fresh read.\nUntrusted content got pars\n[…]\ng the file\nagain. A malformed manifest with no valid signature is now rejected\nas SignatureInvalid rather than leaking InvalidInput from decoding\nuntrusted bytes before the signature was ever checked.",
          "is_bot": false,
          "headline": "Verify manifest signature before parsing it in mere dev import",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T02:17:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "16866d641dd56acbc2db4ad2a2cb630d65dfc04e",
          "body": "…not a second disk read' (#97) from manifest-verify-toctou into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/97",
          "is_bot": false,
          "headline": "Merge pull request 'Parse manifest.v1 from the exact bytes verified, …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T01:35:19Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cbfe0ca354bf3092213aafec2cb9e59dc2e3d5b4",
          "body": "Manifest signature verification hashed/verified the file's bytes in\nmemory and then discarded them; the caller separately re-opened and\nre-read the same path to get bytes for parsing. Swapping the file's\ncontent in that window would make the verified signature meaningless\nfor whatever actually got p\n[…]\nmselves (owned by ctx.allocator) instead of just a fingerprint, so\ninstall.zig can decode the manifest directly from them. Updated the\nother two callers (import.zig, verify.zig) to free the new field.",
          "is_bot": false,
          "headline": "Parse manifest.v1 from the exact bytes verified, not a second disk read",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T01:13:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0121943bc849df74ff3015f7fec507ab11717abd",
          "body": "…before/after atomic rename' (#96) from fsync-store-admission into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/96",
          "is_bot": false,
          "headline": "Merge pull request 'fsync staged package content and store directory …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-09T00:41:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b8ab09a4275ec30a0f9944ae8e917bbd0fe00c6e",
          "body": "…rename\n\nStore admission renamed staged package content into its final\ncontent-addressed path with no fsync anywhere in the sequence, so a\npower loss right after \"install succeeded\" could leave a store object\nwith truncated or corrupted data despite appearing to exist. Recursively\nfsync the staging tree's file data and directory entries before the\nrename, and fsync the store's parent directory afterward to make the\nrename itself durable.",
          "is_bot": false,
          "headline": "fsync staged package content and store directory before/after atomic …",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-09T00:07:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6f293ff61a9ef1cb6a1830da792f5c51580ebc70",
          "body": "…ardcoded exit codes' (#95) from cli-error-boundary-helper into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/95",
          "is_bot": false,
          "headline": "Merge pull request 'Consolidate CLI error-boundary handling and fix h…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-08T23:09:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a58c162136b186b32a97bbb32b3e89606866b24f",
          "body": "install.zig, uninstall.zig, profile.zig, and shell.zig each duplicated\nthe same ~30-line block mapping a caught error to a CommandResult:\nresolve the diagnostic context, format a user-friendly message, dupe\nit, done. Every call site hardcoded exit_code = 1 regardless of the\nactual error, so a Permis\n[…]\nd Zig 0.16 rejects that for a standalone test root). Added real\nregression tests for errorResult's exit-code mapping and diagnostic\ncontext folding; confirmed the exit-code test fails without the fix.",
          "is_bot": false,
          "headline": "Consolidate CLI error-boundary handling and fix hardcoded exit codes",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-08T22:17:38Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "51dbb52870ebd92f99d57ada9b9bb51bfd27907c",
          "body": "…le with a passthrough command' (#94) from shell-passthrough-profile-arg into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/94",
          "is_bot": false,
          "headline": "Merge pull request 'Fix mere shell ignoring the positional profile fi…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-08T21:43:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ddf79419fb99588511536cacd9eb9604189f7f7d",
          "body": "…h command\n\nresolveProfileName only consulted the positional profile.kdl argument\nwhen args.passthrough was empty, but the command's own usage message\ndocuments the file and a passthrough command being used together\n(`mere shell [profile.kdl] -- <command> [args...]`). Using that\ndocumented form sile\n[…]\nperly-scoped\nsrc/cli/-rooted module wired through build.zig - worth doing, but a\nseparate piece of work from this fix. Verified manually by tracing the\nbefore/after logic against the documented usage.",
          "is_bot": false,
          "headline": "Fix mere shell ignoring the positional profile file with a passthroug…",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-08T20:31:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "49aff509c43d960d828aa516f08b35e835710dd5",
          "body": "…y-required packages' (#93) from uninstall-cascade-multiple-packages into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/93",
          "is_bot": false,
          "headline": "Merge pull request 'Fix uninstall --cascade for multiple independentl…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-08T18:23:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ddfc9768e536cd3aa232f88834b5aaf8e9292946",
          "body": "Cascade only ever accounted for the first requested package still\nfound in the resolution: it computed one cascade round, re-resolved\nonce, then unconditionally broke out of the loop. A second requested\nremoval still pulled in by a different, unrelated dependent was never\nre-checked against the new \n[…]\n roots left to re-resolve.\n\nAdded a regression test with two packages independently required by\ndifferent dependents; confirmed it reproduces both bugs independently\nbefore landing the fix for either.",
          "is_bot": false,
          "headline": "Fix uninstall --cascade for multiple independently-required packages",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-08T18:13:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "92b36634199bd5d8bcb28e04b9a3d923cc4624e2",
          "body": "…rsions' (#92) from fix-cross-package-provider into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/92",
          "is_bot": false,
          "headline": "Merge pull request 'Fix cross-package provider selection comparing ve…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-04T12:58:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "950a080d3e57d962a5fb9e7b3c1f0e1ae658ebb4",
          "body": "When multiple packages provide the same soname (e.g. libglvnd and\nmesa both providing libEGL.so.1), compareCandidates sorted them by\nversion number. This is meaningless across different package names —\n\"26.0.3\" vs \"1.7.0\" says nothing about which is the better provider.\nThe result was that mesa alwa\n[…]\ner than hiding it behind\nmeaningless version comparison.\n\nTests:\n- Cross-package providers, different priority -> higher-priority wins\n- Cross-package providers, same priority -> ConflictingProvisions",
          "is_bot": false,
          "headline": "Fix cross-package provider selection comparing versions",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-04T04:14:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "59d8878e927ba6cec3061236eb507a839d7f1676",
          "body": "…not just on download' (#91) from reverify-cached-repo-db into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/91",
          "is_bot": false,
          "headline": "Merge pull request 'Re-verify cached repo.db every time it's opened, …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-03T19:56:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7510358b2c61abb31be14914ca7e4ce42b99cf37",
          "body": "sync() only verified signatures when it actually downloaded a fresh\ndb. Once cached, a repo.db was trusted forever afterward with no\nre-check - ensureRepository() just opened it. A repo.db tampered with\ndirectly on disk after being verified (local attacker, or corruption)\nwould go undetected on ever\n[…]\nall.zig\nand search.zig.\n\nAdded a regression test: a repo verified once, then tampered with\non-disk without a following sync, must be rejected on the next fresh\nopen. Verified it fails without the fix.",
          "is_bot": false,
          "headline": "Re-verify cached repo.db every time it's opened, not just on download",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-03T16:30:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e00173c905e99ff6deaf20bcbe196c0b6dc89eb3",
          "body": "…onfig.kdl' (#90) from verify-local-repo-signature into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/90",
          "is_bot": false,
          "headline": "Merge pull request 'Verify signatures for file:// repos declared in c…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-03T14:44:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0c29606496be35c0affb8b09d24aac1e6f98a54a",
          "body": "syncLocal() was a no-op: sync() on a local (file://) repo never\nverified anything. Auto-discovered dev repos under /mere/dev/repo/\nare safe because repo_sources.zig verifies before ever constructing a\nRepoCache, but a file:// repo declared directly in config.kdl reached\nthis path with its own truste\n[…]\nlocal sync verified nothing.\nFixed both to sign the file that's actually read. Added a regression\ntest proving a local repo signed with an untrusted key is rejected;\nverified it fails without the fix.",
          "is_bot": false,
          "headline": "Verify signatures for file:// repos declared in config.kdl",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-03T14:25:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d07c11f5e119850f00a22c7343bb83c4fb92784c",
          "body": "…from generation-keep-gc-root into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/89",
          "is_bot": false,
          "headline": "Merge pull request 'Create GC roots when keeping a generation' (#89) …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-03T13:29:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "13604e6d12b0efcf727e9a4c4f5cc2575a5662c5",
          "body": "…from remove-system-init-flag into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/88",
          "is_bot": false,
          "headline": "Merge pull request 'Remove --system flag from mere store init' (#88) …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-03T13:18:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e3731bb33872c0d724dfdbceb7275b5bdfa83aa7",
          "body": "createSystemLayout operated on hardcoded absolute host paths (/bin,\n/lib, /etc, ...) regardless of --root, making it the only place in the\ncodebase that could touch the real host filesystem instead of staying\nscoped to the configured root - and a real risk when bootstrapping a\nnew system into a moun\n[…]\nnagement and belongs in a separate installer,\nwhich will need to exist anyway. mere store init now only ever touches\npaths under --root.\n\nNo dedicated tests existed for --system or createSystemLayout.",
          "is_bot": false,
          "headline": "Remove --system flag from mere store init",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-03T13:06:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d9b5bc7a1a9edfaaf7a7c6e7c176eda85c9dd3ee",
          "body": "…kages' (#87) from verify-archive-hash-before-extract into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/87",
          "is_bot": false,
          "headline": "Merge pull request 'Verify archive_hash before caching downloaded pac…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-03T12:49:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3858051761434d4e2ae9515cf05fb3b7db2a0a13",
          "body": "prefetchMissingPackageArchives and ensurePackageArchiveCached both\ndownloaded package archives with DownloadOptions{} - no expected_hash -\neven though the signed repo db already carries the correct archive_hash\nfor every package. A tampered mirror or corrupted transfer would be\nsilently cached and l\n[…]\nt\ndidn't match their dummy archive bodies; fixed them to use the real\nhash of the body now that it's actually checked. Added a regression\ntest that a tampered archive gets rejected before it's cached.",
          "is_bot": false,
          "headline": "Verify archive_hash before caching downloaded packages",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-03T12:18:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9d03ab04dbda12b926f0031c661d4fa7eddaa836",
          "body": "handleKeep only wrote the .keep marker file, never a GC-root symlink.\nmere store clean's reachability walk only follows gc-roots symlinks, so\na kept generation's directory would survive pruning but the store\nobjects it references would still get deleted - breaking the advertised\nrollback. Sync GC roots via gcroots.updateRoots (which already accounts\nfor explicit keeps) right after keepGeneration/unkeepGeneration.",
          "is_bot": false,
          "headline": "Create GC roots when keeping a generation",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-02T17:10:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1876cf92eb139d3426a766247e2be8e81bd89565",
          "body": "…otless-store-init into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/85",
          "is_bot": false,
          "headline": "Merge pull request 'Allow rootless mere store init' (#85) from fix/ro…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-02T16:42:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "affef78407bf9eb0b4577e491d196c0aff341473",
          "body": "…ce log error handling' (#86) from fix/store-mutation-locking into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/86",
          "is_bot": false,
          "headline": "Merge pull request 'Serialize mutating store operations and fix servi…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-07-02T16:41:40Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "35e78a3826c5b506c71d15a5582b27f0d76865fb",
          "body": "Add a process-wide store lock (blocking exclusive flock on\n<root>/mere/.lock, reentrant within a Context) and wire it into every\nmutating command: install, uninstall, store generation\nactivate/keep/unkeep/delete, and store clean. Concurrent mere\ninvocations against the same root were previously unse\n[…]\net unnarrowed - only the exe target reaches this\npath, so zig build test never caught it) and a diagnostic-swallowing\nbug in serviceFailure's PermissionDenied branch that dropped context\nset upstream.",
          "is_bot": false,
          "headline": "Serialize mutating store operations and fix service log error handling",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-07-02T16:29:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f7762666c7f623c94e38e519d1d303a204a8b379",
          "body": "`mere store init` gated on uid 0 and unconditionally chowned every\ndirectory to root:root. With a user-writable MERE_ROOT this gating\ncontributed nothing (filesystem permissions already control access)\nbut it blocked rootless installs entirely.\n\n- Drop the uid 0 check in `init.initialize()`. Filesys\n[…]\nre-test profile create test` succeeds\n- `mere --root /tmp/mere-test install -p test busybox` downloads,\n  signature-verifies, extracts to store, materializes profile\n- Profile binary runs without sudo",
          "is_bot": false,
          "headline": "Allow rootless mere store init",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-26T19:34:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8a1dc01df76a5c545c035659205ecf688e51e6f3",
          "body": "…refactor/drop-rollback-protection into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/84",
          "is_bot": false,
          "headline": "Merge pull request 'Drop rollback protection enforcement' (#84) from …",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-25T14:28:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "946f7870c2912a559461e287e28b84136744432d",
          "body": null,
          "is_bot": false,
          "headline": "Merge branch 'main' into refactor/drop-rollback-protection",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-25T13:58:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "709a3a84b164c726fa64a93bd0b850f58c2752f1",
          "body": "… refactor/declared-arch-only into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/83",
          "is_bot": false,
          "headline": "Merge pull request 'Require explicit package architecture' (#83) from…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-25T13:57:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "09a3b8e72cca94671498d0ca2ec965ff5a92d57b",
          "body": "Rollback protection turned out to be a poor fit for Mere at its\ncurrent scale. The protection it offered (rejecting older signed\npackages on install) defends against a narrow attack: a mirror or\nnetwork attacker substituting an older, validly-signed package for\na newer one. With one repo, one signin\n[…]\nollback bullets and prose throughout\n- Mark created_at as informational\n\nManifest schema is unchanged. Generation rollback (the user-facing\nmere store generation activate flow) is unrelated and stays.",
          "is_bot": false,
          "headline": "Drop rollback protection enforcement",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-25T13:55:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3375335359a24ebdabf738a6d0592653cc8c35c6",
          "body": "Recipes now declare which architectures they support. The\nprevious design tried to infer the target arch from package\ncontents, with \"any\" as a fallback when nothing was\ndetectable. That experiment didn't pay off. Auto-detection\nconfirmed what was already known in the easy cases, but got\nit wrong in\n[…]\nit is simpler and safer. \"any\" is now an opt-in\nclaim that a recipe author makes deliberately. Without a\ndeclaration, a package takes the build host's architecture.\n\nDependency inference is unchanged.",
          "is_bot": false,
          "headline": "Require explicit package architecture",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-25T12:49:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "fdbc5c8268fcd97972addd4363cb0fbf10abc641",
          "body": "…er-abstraction into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/82",
          "is_bot": false,
          "headline": "Merge pull request 'init provider abstraction' (#82) from init-provid…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-09T15:57:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "390c20204f9a9be515a40f7f623a57828ad82f3f",
          "body": "Keep init-provider configuration errors specific as they pass through service management. Service commands now report unsupported configured providers and format config diagnostics instead of collapsing provider and config failures into generic service errors.\n\nAlso include the invalid provider value in config diagnostics so mistakes point directly at the setting that needs correction.",
          "is_bot": false,
          "headline": "Preserve service provider diagnostics",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-09T11:54:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d9dd0c8e5321141093b74d22ef402b9202111db3",
          "body": "Add a typed init-provider setting to Mere configuration and route both runtime service commands and package-time service artifact generation through it.\n\ns6-rc remains the default and only implemented provider. dinit is recognized as a provider name, but selecting it now fails explicitly at the provider boundary instead of silently emitting or executing s6-rc behavior.\n\nAlso include services.zig in the aggregate test root so service facade tests run with the full suite.",
          "is_bot": false,
          "headline": "Wire init provider selection",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-09T11:54:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "35e62f7eb4cecc95f0707a76496922cb667bae9e",
          "body": "Add an active-provider entry point for package-time service artifact generation and move the existing s6-rc source directory writer behind s6-rc-specific function names.\n\nBehavior is unchanged: recipe service blocks still emit s6-rc source directories under usr/share/s6-rc/sources. This makes the package-time provider boundary explicit before adding alternate artifact generators such as dinit.",
          "is_bot": false,
          "headline": "Name s6-rc service artifact generation",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-09T11:54:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "43a215b03fc1ea44102ad1802035f8713ea268c1",
          "body": "Move service command handling behind a provider-neutral services module. The CLI no longer issues s6-rc commands directly for lifecycle, status, listing, or log operations; it delegates to src/services.zig instead.\n\nThe current provider remains s6-rc and behavior is intended to stay the same. This creates the runtime boundary needed before adding alternate init providers such as dinit.",
          "is_bot": false,
          "headline": "Introduce service provider facade",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-09T11:54:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "42c2118ce9f1cfeb7afa694cb5b334f1c93e4ef4",
          "body": "…#81) from fix/bubble-file-hash-errors into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/81",
          "is_bot": false,
          "headline": "Merge pull request 'Bubble file path into calculateFileHash errors' (…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-09T11:49:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bb7e47c03825bf216ff54e261182c5e033d0f329",
          "body": "calculateFileHash now takes *Context instead of Allocator and calls\nctx.setDiagnosticContext with the failing path and original error name\non any I/O failure. This ensures users see which file failed and why,\nrather than a generic \"filesystem error\" with no context.\n\nAlso removes redundant allocator\n[…]\npy,\nwhich caused arena allocations to escape cleanup.\n\nRoot cause of #75: missing signing key mapped FileNotFound to FileSystem\nwith no path context preserved. User now sees the actual path and error.",
          "is_bot": false,
          "headline": "Bubble file path into calculateFileHash errors",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-08T17:11:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dfae3e9a62f6d58304fcb67bccc2e030fd472f7a",
          "body": "Reviewed-on: https://codeberg.org/merelinux/mere/pulls/80",
          "is_bot": false,
          "headline": "Merge pull request 'release: v0.14.0' (#80) from v0.14.0 into main",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-04T17:07:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8e49dfe30bfcaecf08ce546a9d2ec419b7423cbf",
          "body": null,
          "is_bot": false,
          "headline": "release: v0.14.0",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-04T16:45:54Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c3dc7f90854363f0221e569c9847c957fe61750f",
          "body": "…ror-messages into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/79",
          "is_bot": false,
          "headline": "Merge pull request 'fix/cache-error-messages' (#79) from fix/cache-er…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-04T16:44:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b5411fa19b77f3e7448d6050eb048eb4e43820dc",
          "body": "The rootless support PR (#78) added a guard that skips\nrollback-state updates when running unprivileged. The\nexisting \"fails hard on rollback-state update after\nadmission\" test relied on running unprivileged and chmod'ing\nthe cache directory to trigger PermissionDenied — the new\nguard makes that pat\n[…]\nvileged, finalizeAdmittedStoreObject succeeds and\ndoes not write a rollback-state file. Error propagation in\nthe privileged path is the standard switch-and-return pattern\nused throughout the codebase.",
          "is_bot": false,
          "headline": "Test the rootless guard in finalizeAdmittedStoreObject",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-04T16:36:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ed85634accaecfebee375ad160869379569952fb",
          "body": "When restorePackageArchiveRequest failed, the same generic\n\"failed to resolve package cache\" message was returned for\nevery CacheError variant (OutOfMemory, FileSystem,\nPermissionDenied, InvalidInput). Users hitting the failure\nhad no way to tell whether they were looking at a permissions\nproblem, a\n[…]\nOOM, or invalid input.\n\nReplace the catch-all `else` branch with explicit handling\nof each variant, surfacing a distinct hint in the diagnostic\nmessage. The PackageError mapping is unchanged.\n\nRef #75",
          "is_bot": false,
          "headline": "Distinguish CacheError variants in package staging",
          "author_name": "Jeremy Huntwork",
          "author_login": "jhuntwork",
          "committed_at": "2026-06-04T16:36:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "34d84905b559b7c2c262c746cb386cb69657d06a",
          "body": "…t into main\n\nReviewed-on: https://codeberg.org/merelinux/mere/pulls/78",
          "is_bot": false,
          "headline": "Merge pull request 'rootless-support' (#78) from feat/rootless-suppor…",
          "author_name": "jhuntwork",
          "author_login": null,
          "committed_at": "2026-06-03T23:31:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 32,
      "commits_last_year": 272,
      "latest_release_at": "2026-07-21T01:53:28Z",
      "latest_release_tag": "v0.16.1",
      "releases_from_tags": true,
      "days_since_last_push": 10,
      "active_weeks_last_year": 14,
      "days_since_latest_release": 10,
      "mean_days_between_releases": 11.9
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 42,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": []
    },
    "popularity": {
      "forks": 0,
      "stars": 5,
      "watchers": 2,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "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": 441,
      "source_files_sampled": 1,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [],
      "advisories": {
        "error": "No resolved dependencies to assess",
        "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": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [],
      "dependencies": [],
      "all_dependencies": {
        "error": null,
        "source": "github-sbom",
        "packages": [],
        "collected": true,
        "truncated": false,
        "total_count": 0,
        "direct_count": 0,
        "indirect_count": 0
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 15,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 1,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "jhuntwork",
          "commits": 167,
          "avatar_url": "https://avatars.githubusercontent.com/u/239626?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": false,
      "has_tests": true,
      "ci_workflows": [],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "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": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": null,
            "reason": "no workflows found",
            "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 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": null,
            "reason": "no dependencies found",
            "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": null,
            "reason": "No tokens found",
            "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": "83bd82c434c2b7bcc854f6dc750206d970427025",
        "ran_at": "2026-07-31T12:16:29Z",
        "aggregate_score": 3.7,
        "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-21T01:53:43Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2024-07-23T04:12:55Z",
      "ci_last_conclusion": null,
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/jhuntwork/mere",
    "host": "github.com",
    "name": "mere",
    "owner": "jhuntwork"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 50,
      "inputs": {
        "security": 37,
        "vitality": 75,
        "community": 29,
        "governance": 48,
        "engineering": 54
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 75,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 66,
            "inputs": {
              "commits_last_year": 272,
              "human_commit_share": 1,
              "days_since_last_push": 10,
              "active_weeks_last_year": 14
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 10 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 10
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "14/52 weeks with commits",
                "points": 9.7,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 14
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "272 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 272
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 88,
            "inputs": {
              "releases_count": 32,
              "latest_release_tag": "v0.16.1",
              "releases_from_tags": true,
              "days_since_latest_release": 10,
              "mean_days_between_releases": 11.9
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "32 version tags (no GitHub releases)",
                "points": 16.2,
                "status": "partial",
                "details": [
                  {
                    "code": "version_tags_no_releases",
                    "params": {
                      "count": 32
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 10 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 10
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~11.9 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 11.9
                    }
                  }
                ],
                "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": 10,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 10 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 10
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "critical",
        "name": "Community & Adoption",
        "value": 29,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "forks": 0,
              "stars": 5,
              "watchers": 2,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "5 stars",
                "points": 9.8,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "2 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.3
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 48,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "excellent",
            "name": "Issue & PR responsiveness",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "merged_prs": 15,
              "open_issues": 0,
              "closed_issues": 1,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 0
            },
            "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": "15/15 decided PRs merged",
                "points": 38.2,
                "status": "met",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 15,
                      "decided": 15
                    }
                  }
                ],
                "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": "moderate",
            "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": 55,
            "inputs": {
              "followers": 42,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "jhuntwork",
              "public_repos": 23,
              "account_age_days": 5957
            },
            "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": "42 followers of jhuntwork",
                "points": 11.7,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 42,
                      "login": "jhuntwork"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "23 public repos, account ~16 yr old",
                "points": 22.1,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 23
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 16
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "moderate",
        "name": "Engineering Quality",
        "value": 54,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "at_risk",
            "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": 30,
            "inputs": {
              "has_ci": false,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "excellent",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": "https://codeberg.org/merelinux/mere",
              "has_readme": true,
              "has_docs_dir": true,
              "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": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://codeberg.org/merelinux/mere",
                "points": 15,
                "status": "met",
                "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": 37,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Dangerous-Workflow, Packaging, Pinned-Dependencies, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "dangerous_workflow",
                    "packaging",
                    "pinned_dependencies",
                    "signed_releases",
                    "token_permissions"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 37,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 12,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 6,
              "scorecard_aggregate": 3.7
            },
            "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 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no workflows found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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": "No tokens found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "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": 2
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "at_risk",
        "name": "AI Readiness",
        "value": 35,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.99,
              "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": "99 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 99,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "critical",
            "name": "Verify loop (build / test / typecheck)",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Pinned-Dependencies. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_pinned_dependencies"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 24,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "no dependencies found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "Zig",
              "largest_source_bytes": 441,
              "source_files_sampled": 1,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Zig without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "Zig"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/1 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 1,
                      "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": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-31T12:17:04.796925Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/j/jhuntwork/mere.svg",
  "full_name": "jhuntwork/mere",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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