公开记录
软件健康报告模式 0.27.0 · 指标 1.13.0 · 2026-07-30 13:47 UTC

juergenbeck / XrmForge

TypeScriptMIT★ 1 星标⑂ 0 复刻始于 2026年3月在 GitHub 上查看 ↗

juergenbeck/XrmForge 的健康指数为 100 分中的 46 分,处于「存在风险」区间。 其得分最高的类别是AI Readiness(71/100),最低的是Sustainability & Governance(32/100)。 最近一次更新在 3 天前。 近期的大部分工作由 1 位贡献者完成。

46
总分 / 100
存在风险

软件健康指数

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

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

评分画像

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

所有权

juergenbeck个人账户
0 关注者3 个公开仓库始于 2016年6月

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

软件包生态系统

按类别列示的指标

活力

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

43存在风险 · 占总体的 22%
评分方式
36/36推送新近度 — 最近一次推送于 3 天前
6.9/36提交节奏 — 52 周中有 10 周有提交
18/18提交量 — 最近一年 250 次提交
10/10OpenSSF Scorecard:Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
所用输入
commits_last_year250
human_commit_share1
days_since_last_push3
active_weeks_last_year10
评分方式
0/27有发布版本 — 未发布任何发布版本
0/36发布时效 — 没有发布版本
0/27发布节奏 — 没有发布版本
0/10OpenSSF Scorecard:Signed-Releases — 无数据
所用输入
releases_count0
已排除计分(无数据或不适用):OpenSSF Scorecard:Signed-Releases。 其余权重已重新归一化。

社区与采用

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

44存在风险 · 占总体的 18%
评分方式
0/60星标 — 1 个星标
0/25复刻 — 0 个复刻
0/15关注者 — 0 位关注者
所用输入
forks0
stars1
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history

社区健康

77良好
评分方式
22.5/22.5README
22.5/22.5许可证 — 可识别的许可证(MIT)
18/18CONTRIBUTING 指南
0/13.5行为准则
0/7.2议题模板
6.3/6.3PR 模板
所用输入
has_readme
has_license
has_contributing
has_issue_template
has_code_of_conduct
has_pull_request_template
评分方式
51.7/80月度下载量 — npm 合计每月 7,492 次下载
0/20注册表被依赖数 — 该生态系统不报告此项
所用输入
packages@xrmforge/cli, @xrmforge/devkit, @xrmforge/webapi, @xrmforge/helpers, @xrmforge/testing, @xrmforge/typegen, @xrmforge/eslint-plugin, @xrmforge/dataverse-core
dependents
ecosystemsnpm
total_downloads
monthly_downloads7,492
已排除计分(无数据或不适用):注册表被依赖数。 其余权重已重新归一化。

可持续性与治理

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

32存在风险 · 占总体的 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
评分方式
0/46.8议题解决 — 没有议题或无数据
0/38.3PR 接受 — 没有已裁定的拉取请求或无数据
0/15OpenSSF Scorecard:Code-Review — Found 0/30 approved changesets -- score normalized to 0
所用输入
merged_prs0
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
已排除计分(无数据或不适用):议题解决, PR 接受。 其余权重已重新归一化。
评分方式
10/30所有权背书 — 个人(用户)账户
0/20已验证域名 — 不适用于个人账户
0/25所有者影响力 — juergenbeck 有 0 位关注者
16.4/25既往记录 — 3 个公开仓库,账户约 10 年
所用输入
followers0
owner_typeUser
is_verified
owner_loginjuergenbeck
public_repos3
account_age_days3,710
已排除计分(无数据或不适用):已验证域名。 其余权重已重新归一化。
评分方式
25/25已发布且可解析 — npm 上有 8 个软件包
35/35发布时效 — 最近一次发布于 8 天前
20/20版本历史 — 60 个已发布版本
20/20未被弃用 — 活跃,未被弃用或撤回
所用输入
packages@xrmforge/cli, @xrmforge/devkit, @xrmforge/webapi, @xrmforge/helpers, @xrmforge/testing, @xrmforge/typegen, @xrmforge/eslint-plugin, @xrmforge/dataverse-core
ecosystemsnpm
any_deprecated
min_days_since_publish8

工程质量

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

70良好 · 占总体的 20%

工程实践

80良好
评分方式
24/24CI 工作流 — 2 个工作流
24/24存在测试
16/16Linter 配置 — eslint.config.js, eslint.config.mjs
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。 其余权重已重新归一化。

文档

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

安全

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

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

安全态势

31存在风险
评分方式
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
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5许可证 — license file detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — 无数据
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — 无数据
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 20 existing vulnerabilities detected
所用输入
sourceopenssf_scorecard
checks_evaluated15
scorecard_versionv5.5.0
checks_inconclusive3
scorecard_aggregate3.1
已排除计分(无数据或不适用):ci_tests, packaging, signed_releases。 其余权重已重新归一化。
评分方式
35/35直接依赖不含已知公告 — 没有直接依赖携带已知公告
25/25间接依赖不含已知公告 — 没有间接依赖携带已知公告
0/40没有长期未处理的公告 — 没有公告带有发布日期
所用输入
sourceosv
advisories0
affected_packages0
assessed_packages82
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
已排除计分(无数据或不适用):没有长期未处理的公告。 其余权重已重新归一化。 比对的是 npm:@xrmforge/cli@0.10.8 的运行时依赖闭包——安装已发布的软件包时真正被拉取进来的内容——共 82 个软件包。 未对可达性进行分析。

AI 就绪度

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

71良好 · 占总体的 0%
评分方式
45/45代理指令 — CLAUDE.md, packages/devkit/src/scaffold/templates/AGENT.md
0/15机器可读文档(llms.txt)
40/40可读的提交历史 — 100 次人类提交中有 92 次说明了意图(结构化标题或解释性正文)
所用输入
has_llms_txt
legible_history_share0.92
agent_instruction_filesCLAUDE.md, packages/devkit/src/scaffold/templates/AGENT.md
agent_instruction_max_bytes66,013
评分方式
0/18一条命令的引导启动
22/22自动化测试
11/11Lint / 格式化配置 — eslint.config.js, eslint.config.mjs
11/11静态类型检查 — examples/account-form-demo/tsconfig.json, packages/cli/tsconfig.json, packages/dataverse-client/tsconfig.json, packages/dataverse-core/tsconfig.json, packages/devkit/tsconfig.json, packages/eslint-plugin/tsconfig.json, packages/helpers/tsconfig.json, packages/testing/tsconfig.json, packages/typegen/tsconfig.json, packages/webapi/tsconfig.json
10/10可复现环境 — lockfile
8/10已体现的代理实践 — 最近 100 次提交中有 4 次由代理编写或署名代理
0/8自动化维护 — 未观察到自动依赖更新
0/10OpenSSF Scorecard:Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
所用输入
has_nix
has_tests
lockfilespnpm-lock.yaml
has_dockerfile
typed_language
bootstrap_files
has_devcontainer
has_linter_config
typecheck_configsexamples/account-form-demo/tsconfig.json, packages/cli/tsconfig.json, packages/dataverse-client/tsconfig.json, packages/dataverse-core/tsconfig.json, packages/devkit/tsconfig.json, packages/eslint-plugin/tsconfig.json, packages/helpers/tsconfig.json, packages/testing/tsconfig.json, packages/typegen/tsconfig.json, packages/webapi/tsconfig.json
agent_commit_share0.04
toolchain_manifests
dependency_bot_commit_share0
评分方式
45/45可类型检查的代码 — TypeScript(静态类型)
55/55可控的文件大小 — 采样的 197 个源文件中有 0 个超过 60KB
所用输入
primary_languageTypeScript
largest_source_bytes38,309
source_files_sampled197
oversized_source_files0

机器可读接口

40存在风险
评分方式
0/40API 模式(OpenAPI/GraphQL/proto)
0/20MCP 服务器
40/40可运行示例 — examples
所用输入
example_dirsexamples
has_mcp_signal
api_schema_files

关键数据

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

数据采集警告

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

更多细节

OpenSSF Scorecard 3.1 / 10
3.1综合

来自开源项目 OpenSSF Scorecard 的独立、工具无关的安全评估。每项检查奖励的是安全实践本身,而非特定供应商的工具。Scorecard 无法判定的检查项标记为 不适用,并从安全评分中剔除(绝不按零分计)。Scorecard v5.5.0 · 2026-07-30 13:47 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
10Dangerous-Workflowno dangerous workflow patterns detected
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
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
不适用Signed-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities20 existing vulnerabilities detected
直接依赖 9
注册表软件包版本约束清单文件
npm@xrmforge/devkitworkspace:*packages/cli/package.json
npm@xrmforge/typegenworkspace:*packages/cli/package.json
npmcommander^13.0.0packages/cli/package.json
npmdotenv^17.4.2packages/cli/package.json
npm@xrmforge/dataverse-coreworkspace:*packages/dataverse-client/package.json
npmesbuild^0.25.0packages/devkit/package.json
npm@azure/identity^4.6.0packages/typegen/package.json
npm@xrmforge/dataverse-coreworkspace:*packages/typegen/package.json
npmfast-xml-parser^5.5.9packages/typegen/package.json
全部依赖 未采集

本报告未能采集到解析后的依赖集合:GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

依赖安全公告 0

安装 npm:@xrmforge/cli@0.10.8 会引入 82 个包(直接与传递):其中 0 个存在已知公告,0 个为直接依赖。

没有已知公告影响已评估的依赖。

公告表示依赖图中记录的版本落入某条公告的受影响范围。可达性未经分析,且依赖图包含开发与测试的版本固定——某项发现可能只涉及工具链而非交付的软件。

原始 JSON 报告 机器可读
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 1469,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Shell": 9021,
        "Python": 73037,
        "JavaScript": 31640,
        "PowerShell": 3787,
        "TypeScript": 1039141
      },
      "pushed_at": "2026-07-27T00:21:06Z",
      "created_at": "2026-03-28T21:31:57Z",
      "owner_type": "User",
      "updated_at": "2026-07-27T00:21:10Z",
      "description": null,
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "TypeScript",
      "significant_languages": [
        "TypeScript"
      ]
    },
    "owner": {
      "blog": null,
      "name": null,
      "type": "User",
      "login": "juergenbeck",
      "company": null,
      "location": null,
      "followers": 0,
      "avatar_url": "https://avatars.githubusercontent.com/u/19701813?v=4",
      "created_at": "2016-06-02T04:53:20Z",
      "is_verified": null,
      "public_repos": 3,
      "account_age_days": 3710
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [],
      "recent_commits": [
        {
          "oid": "e3fa8e976cd9e049c9ad2f3e3292e4ab55518306",
          "body": "Nach ADR-028 prüft der Hook nur noch hinzugefügte Zeilen. Damit entfällt der\nGrund, eigene Prosa in handover/, changelog/, reviews/, research/, recherche/,\npoc/, _archive/ und 99_archiv/ auszunehmen, ebenso alte-notizen,\nlessons-learned und skeptiker-review. Neue Dateien dort werden jetzt geprüft.\n\n\n[…]\nieden:\nbacklog und planung sind in der Praxis DevOps-Dumps, memory-snapshot ist eine\nKopie, die man nicht anfassen darf.\n\nDieselbe Klassifikation in DEFAULT_EXCLUDE_RE (Wirkung nur im md_only-Profil).",
          "is_bot": false,
          "headline": "generated-Ausnahmen auf ihren eigentlichen Zweck entschlacken",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-27T00:21:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f7c168a0fb9dad37cb6858e605e8278131e61fac",
          "body": "Die zentrale Hook-Kette verbot bisher nur den Rechtspfeil (U+2192),\nMarkants lokale typo_check_lib dagegen alle fünf. Damit war das\nBereinigungswerkzeug fix-typografie.py strenger als der Hook, den es\nbedient, und dieselbe Regel wirkte je nach Repo unterschiedlich.\n\nJetzt einheitlich in commit-msg, \n[…]\nock-typografie.py und\ncheck-tool-umlaute.py: U+2192, U+2190, U+2194, U+21D2, U+21D4.\n\nVerifiziert: alle fünf Zeichen blocken an allen vier Punkten, Zitate in\nBackticks und Code-Fences bleiben erlaubt.",
          "is_bot": false,
          "headline": "Typografie-Sperre auf alle fünf Pfeilzeichen erweitern",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-27T00:05:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a69f9ceaa4bbc7f192b0046b2fcf95dbcdf38ceb",
          "body": "Bisher prüfte der Hook den ganzen Dateiinhalt. Damit verlangte er Änderungen an\neingefrorener Historie, sobald jemand eine alte Datei aus anderem Grund anfasst, und\ndie Pfad-Ausnahmeliste musste jede Ordner-Umbenennung nachziehen (bei handover ->\nsessions ist genau das misslungen).\n\nDer Scan läuft w\n[…]\n\n\nBelegt an drei Fällen: neue Datei mit Verstoß blockiert; Bestandsdatei mit 17\nAlt-Verstößen plus sauberer Zeile läuft durch; Bestandsdatei mit unsauberer neuer\nZeile blockiert genau an dieser Zeile.",
          "is_bot": false,
          "headline": "Pre-Commit prüft ab jetzt nur hinzugefügte Zeilen (ADR-028)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T23:07:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d1a8271821280d4c1075d124c2bc16bf212806a6",
          "body": "Der Stamm \"aussehen\" stand in ss_stems, obwohl das Wort korrekt\ngeschrieben ist (aus plus sehen) und gar keine Variante mit ss hat.\nEr blockte dadurch eine korrekte Commit-Message am Wort \"aussehendes\".\nNach der Pruefregel im Skill umlaute darf nur ein Stamm in die Liste,\ndessen korrekte Form ein scharfes s traegt.",
          "is_bot": false,
          "headline": "Umlaut-Trigger: Fehlalarm-Stamm aussehen entfernt",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T22:36:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "77bf999c430405f443a4c721c3a74879354ba12d",
          "body": "Die Trigger-Liste kannte bisher nur die voll ASCII-isierte Form solcher\nWörter (`groess`, `gemaess`), nicht die halb korrekte Mischform, bei der\nder Umlaut richtig steht und nur das scharfe s durch ss ersetzt ist. Genau\ndiese Schreibweise rutschte am 26.07. unbemerkt in eine Commit-Message.\n\nNeu in \n[…]\n\nein massiver Fehlalarm. Aufgenommen wird nur, wessen korrekte Form ein\nscharfes s trägt.\n\nIsoliert verifiziert: alle vier Stämme blocken einzeln, die Gegenprobe mit\nflüssig und Schlüssel läuft durch.",
          "is_bot": false,
          "headline": "Umlaut-Hooks: Mischformen mit korrektem Umlaut und falschem ss ergänzt",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T19:12:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3cd030c51470a47a2c9b2c4e016cb4a09e31878d",
          "body": "…enommen\n\nDer Stamm für „außerdem\" fehlte in der zentralen Trigger-Liste; eine Commit-Message\nmit dem Surrogat lief ungeblockt durch. Als fc=false ergänzt, wirkt also nur im\ncommit-msg-Hook: die Datei-Treffer liegen überwiegend in eingefrorener Historie\n(Handover- und State-Snapshots), die man nicht\n[…]\nthooks/fix-typografie.py ersetzt verbotene Typografie\nregelbasiert in Markdown, mit derselben Fundstellen-Logik wie der Hook. Damit\nbekommt jedes Repo das Werkzeug über den Sync statt über eine Kopie.",
          "is_bot": false,
          "headline": "Umlaut-Trigger ergänzt und Typografie-Werkzeug in die Hook-Kette aufg…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T16:50:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f547cd8a752786b8f2a5c90e3619b54b17d92e6d",
          "body": "Zentrale Trigger-Liste um den fehlenden Stamm für Wächter ergänzt (wirkt nur im\ncommit-msg-Hook, damit Personennamen in Dateien nicht blockiert werden). Der Sync\nzieht zugleich den seit dem letzten Rollout aufgelaufenen Stand der Kette nach.",
          "is_bot": false,
          "headline": "Umlaut-Hooks: Trigger-Stand 2026-07-26 nachgezogen",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T15:43:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "772a2f4ad10f1dd14e3cc74544511474febf9bcb",
          "body": "Zentral in umlaute-triggers.json auf fc=true gesetzt und per Sync-UmlautTriggers.ps1 ausgerollt. Vorher fing nur die Commit-Message-Prüfung diesen Stamm, die Datei-Inhalt-Prüfung nicht.",
          "is_bot": false,
          "headline": "Umlaut-Trigger: Stamm `waere` gilt jetzt auch für Datei-Inhalte",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-26T09:12:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e7db0023d24dced1a4738ac680e699c8a07426f9",
          "body": "…ommit- und commit-msg-Check)",
          "is_bot": false,
          "headline": "Umlaut-Sync: Typografie-Sperre ausgerollt (block-typografie.py, pre-c…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-25T19:58:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1a753cf915cf34539806c310de0037dc5c1f4a0a",
          "body": "…docs (RETRO13-02/-03, OE-26 option 0)",
          "is_bot": false,
          "headline": "devkit 0.10.2, cli 0.10.8: AGENT.md cross-WebResource + submit-idiom …",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-21T19:48:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "890ac2eebbce0c5e0d1cf01a6a3299b9680015d4",
          "body": "…tValue() without submit (RETRO13-01)",
          "is_bot": false,
          "headline": "devkit 0.10.1, cli 0.10.7: gate + AGENT.md flag off-form $unsafe().se…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-21T19:21:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9947f1c71a8a70294e268c964a0bf510fd33f75c",
          "body": null,
          "is_bot": false,
          "headline": "Release @xrmforge/devkit 0.10.0, cli 0.10.6 (OE-25 tests/ static checks)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T13:41:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4fabeeba846f3c20d9876598b7bbc5716fe6e965",
          "body": "Scaffolded projects excluded tests/ from both tsc (include: src/ + generated/ only)\nand eslint (files: src/**), so test code had no static analysis - only vitest runtime\nexecution. This adds:\n\n- tsconfig.tests.json (extends tsconfig.json, skipLibCheck true, types xrm+node,\n  includes tests/): a seco\n[…]\ne error in a\n  test file fails tsconfig.tests.json).\n\nVerified against the markant showcase: the tests/ typecheck surfaces 49 real,\npreviously-hidden test-code type errors (retrieveRecord mock casts).",
          "is_bot": false,
          "headline": "Typecheck and lint tests/ in scaffolded projects (OE-25)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T13:38:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "428fa0b1d4be897f2f220888dfff69138830c610",
          "body": "Neue Heuristik is_frontmatter_value in der geteilten umlaut_check_lib: ein\neinzelner Skalar-Wert im führenden YAML-Frontmatter (z.B. `name: zellenlaeufer`)\nwird als technischer Bezeichner ausgenommen, ohne Whitelist. Prosa-Werte und\nVorkommen außerhalb des Frontmatters bleiben geprüft. Zentraler Sync-Nachzug\naus dem Template (claudecode 5eb22a1).",
          "is_bot": false,
          "headline": "Umlaut-Check: Frontmatter-Wert-Filter (ADR-2026-07-19-1123)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T10:56:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "86b81eba4d7598a3aa3802da660fbb7eb3d29265",
          "body": "…CONS-02)",
          "is_bot": false,
          "headline": "Release @xrmforge/devkit 0.9.1, cli 0.10.5 (AGENT.md gate note for F-…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T08:07:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14c0d323b76939d7b3508ea04af9ea66c9215eee",
          "body": "…te check)",
          "is_bot": false,
          "headline": "Release @xrmforge/devkit 0.9.0, cli 0.10.4 (F-CONS-02 any-response ga…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T07:17:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6b4fc2c5c813b6ebfacc5824913e20a150ba0bcc",
          "body": "New multi-line check \"Untyped retrieveRecord response\" in validate-form.mjs\n(checkUntypedRetrieveRecord). retrieveRecord<T = any> returns any by default,\nso an uncast response is silently any and no existing gate catches it\n(no-explicit-any, tsc, Check 3p all pass).\n\nThe check scans the whole declar\n[…]\n is flagged; `as any`/`as Record` go to no-explicit-any / Check 3p.\n\nScaffold regression test added. Changeset: devkit minor, cli patch.\nVerified live against markant: 9 real finds, 0 false positives.",
          "is_bot": false,
          "headline": "F-CONS-02: gate check for untyped (any) retrieveRecord responses",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T05:48:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5e8b5fc460821c6a8f42c691be63bb2f64ce000b",
          "body": "….10.3",
          "is_bot": false,
          "headline": "Release @xrmforge/helpers 0.17.0, typegen 0.20.0, devkit 0.8.0, cli 0…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T01:17:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c9277bbda252102603c9ea5cea00eaf9258d5807",
          "body": "…ed gate\n\nhelpers (minor): parseLookup/parseLookups/parseFormattedValue/expanded/expandedMany\ntake WebApiRecord (object & { length?: never }) - a response cast to a generated\nEntity interface goes straight in (no Record cast, never left as any), while a whole\nresult collection passed by mistake (a f\n[…]\n: unknown } (F-CONS-03); AGENT.md\nteaches the direct reader path, documents normalizeGuid, points to form-index.json.\n\ncli (patch): re-pin. Changesets added; NOT published (awaiting release approval).",
          "is_bot": false,
          "headline": "OE-21/OE-22/OE-23 + F-CONS-01/03: reader signatures, form-index, scop…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-19T01:06:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c20be4b99c370e699577e02369e71a4a60dd99a7",
          "body": "… regression)\n\nThe scaffold eslint.config.js now ignores legacy-reference/** in addition to dist/\nand scripts/, so `pnpm lint` (eslint .) stays green on migration-reference code left\nunconverted, aligned with the validate-form gate (eslint src/).\n\nThe Session-71 fix (A71-03) lived only in the generated showcase eslint.config.js\n(untracked) and was lost on the round-13 reset; moving it into the devkit template\nmakes it durable. Adds a scaffold regression test.\n\ndevkit 0.7.41, cli 0.10.2 (patch).",
          "is_bot": false,
          "headline": "Scaffold eslint: ignore legacy-reference/** (fixes F-MK13-01 / A71-03…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-18T12:03:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "91837036a23bdbe87a4765124f554801a0754627",
          "body": "Hebt die generierten Hook-Dateien (commit-msg, umlaut_check_lib.py,\ncheck-tool-umlaute.py, README) auf den aktuellen Stand der zentralen\nTrigger-Liste und Templates. Neu im Dateinamen-Erkenner der Lib: die\nDiagramm-Endungen (drawio, mmd, mermaid, puml, plantuml, vsdx) plus\nJSON-Key-Filter, damit solche Dateinamen nicht mehr als Umlaut-Verstoß\ngewertet werden.",
          "is_bot": false,
          "headline": "Umlaut-Hook-Kette auf zentralen Template-Stand aktualisiert",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-12T20:16:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "48f1b5a872b16ed1677f232d02a5ba7f9e16eabc",
          "body": "… scaffold)",
          "is_bot": false,
          "headline": "Release @xrmforge/devkit 0.7.40, @xrmforge/cli 0.10.1 (wrapEnableRule…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-08T14:01:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "45f4df4c7649146e17da04cd8801b18ddeb360d2",
          "body": "A ribbon Enable Rule must be synchronous and return a boolean; an async rule\nreturns a truthy Promise (the button is then always shown - a common legacy bug).\nwrapEnableRule wraps it fail-closed (false on error), logs only, never a banner\n(it runs on every ribbon refresh). Added to the error-handler.ts template, the\nvalidate-form.mjs HANDLER_WRAPPERS gate, the AGENT.md instructions and the\nscaffold test. Changeset: devkit patch.",
          "is_bot": false,
          "headline": "Add wrapEnableRule to devkit scaffold for ribbon Enable Rules",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-08T13:52:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6c978a59fbc3160960609864d8ce218c72b939cd",
          "body": "helpers 0.16.0: typedField(fc, field, kind) for variable field names,\navoiding the F-LMA12-01 kindMap union trap (OE-17 b). test-d proves all\nseven kinds stay exact.\n\ntypegen 0.19.0 + cli 0.10.0: generateFieldKinds is opt-in (default off,\n--field-kinds / config fieldKinds); XxxExpands unchanged (OE-\n[…]\n matches the gate (F12-03),\nAGENT.md updates (F12-02 seven AttrKinds, typedField union trap, FieldKinds\nopt-in, F-MK12-01 wrap* are local). Scaffold pins cli ^0.10.0 / helpers ^0.16.0.\n\nNot published.",
          "is_bot": false,
          "headline": "Runde 12 fix stack: typedField, FieldKinds opt-in, kindMap gate",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-08T04:30:44Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a582422cf8660c89f83ab0a137f251909dc34221",
          "body": "…re (OE-15)\n\nDataverseHttpClient now runs retry/backoff/rate-limit/timeout through the shared\nResilientRunner from @xrmforge/dataverse-core (new dependency), over a Node\nbearer-token NodeTransport that implements the core DataverseTransport. The\nNode-only concerns the browser-lean runner omits stay \n[…]\nons (Juergen): scope S2 (full migration, not S1), error-wrap E1, 401-recovery\nlocation 401-C (typegen wraps the runner call; core stays browser-lean).\n\ntypegen 0.18.0 (MINOR), cli 0.9.3 (propagation).",
          "is_bot": false,
          "headline": "typegen: migrate HTTP client + sanitizers onto @xrmforge/dataverse-co…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-06T13:57:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1bcfec8fe8eb260b046b5449c9f1aec6f71d3973",
          "body": "…it 0.7.38, cli 0.9.2)\n\nhelpers: add typedFields(fc, kindMap) - the typed, NULLABLE counterpart to typedForm\nfor genuinely cross-entity / cross-form scripts where no single FormTypeInfo fits\n(Option B). The kindMap drives per-field types; every accessor is nullable (honest,\nunlike a per-entity union\n[…]\nlue/setValue layer as the Rule-19 anti-pattern it replaces; scaffold\nhelpers pin ^0.15.0. cli: propagate typegen 0.17.0 + devkit 0.7.38.\n\nAll 4 gates green (build/test/typecheck/lint); monorepo 18/18.",
          "is_bot": false,
          "headline": "OE-16: cross-entity typedFields (helpers 0.15.0, typegen 0.17.0, devk…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-06T06:48:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "66e3cf13fb198ceae7b6de2d1d4e28e7e8cb8eef",
          "body": "…ified)\n\ntypegen 0.16.0 (MINOR): keep standalone EntityName fields (AttributeOf null, e.g. activitytypecode); still exclude lookup companions (owneridtype, regardingobjecttypecode). Verified live against markant-dev. Fix-comment example corrected to the real property name regardingobjecttypecode.\n\nd\n[…]\n0.9.1 (PATCH): propagate typegen 0.16.0 + devkit 0.7.37.\n\nCHANGELOGs: add new entries and backfill the missing 0.15.0/0.7.36/0.9.0 entries (stale since the session-56 publish, npm-publish pitfall 13).",
          "is_bot": false,
          "headline": "Release typegen 0.16.0, devkit 0.7.37, cli 0.9.1 (F-LMA11-04 live-ver…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-06T04:42:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "44e7d2caaeb899ac5d93d512fb3f05fd4635128f",
          "body": "…idate-form 3n)\n\n- F-MK11-01: scaffold.ts pins cli ^0.9.0, testing ^0.8.0 (current minors)\n- F-MK11-02: AGENT.md template polymorphic @odata.bind uses target-qualified XxxExpands (4 spots)\n- validate-form 3n: FetchXML attribute= regex matches double-quote too, [a-z] interpolation guard\n- F-LMA11-04:\n[…]\nCT; CACHE_VERSION 3->4\n- Tests: type-mapping companion/standalone split, entity-generator fixture realistic\n\nLive verification of F-LMA11-04 (activitytypecode AttributeOf null) pending before publish.",
          "is_bot": false,
          "headline": "Framework fixes from showcase round 11 (F-MK11-01/02, F-LMA11-04, val…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-06T03:54:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0df6ae70a490c1c4a9f15e74b68fdedcf76aafba",
          "body": "…ransport\n\nAdd @xrmforge/dataverse-core@0.1.0 (runtime-neutral: OData sanitizers,\nDataverseTransport interface, browser-lean ResilientRunner, error types).\n\nAdd @xrmforge/dataverse-client@0.1.0 (BrowserTransport cookie-fetch verified\nagainst a live org, retrieveAll nextLink paging with a 100-page sa\n[…]\nher with the typegen client runner migration, since delegating\nwould switch the thrown error type from ApiRequestError to DataverseError).\n\nAll gates green per package; monorepo build 9/9, test 18/18.",
          "is_bot": false,
          "headline": "OE-15 stage 1: dataverse-core + dataverse-client (retrieveAll), FakeT…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-05T22:02:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "151aed714060a11ddc571663423e619340a15fb5",
          "body": "Familien-Standard nach Palas-Vorbild (claudecode-ADR-2026-07-04-0943): Füllung aus dem\njüngsten usage-Eintrag des Transcripts, auch nach Compact korrekt. Schwellen 50/70/85,\nFenster via CLAUDE_CONTEXT_WINDOW. Repo-eigener Übergabe-Hinweis ersetzt die toten\nVerweise auf den nur in Markant vorhandenen Skill claude-code-optimierung.",
          "is_bot": false,
          "headline": "feat(hooks): Kontextlast misst echte usage-Token statt Byte-Heuristik",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-04T07:57:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e7cbd90b3305605beb8abfcc7b9e4e61a6dc0759",
          "body": "… Mojibake am Handover-Regex an",
          "is_bot": false,
          "headline": "fix(hooks): stdin auf UTF-8 rekonfigurieren, Umlaut-Prompts kamen als…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-07-04T07:18:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "32a49969ff93b06e22f7df95a5f6bc30a760335d",
          "body": "check-tool-umlaute.py (permissionDecision deny, eingebettete Trigger-Daten, AskUserQuestion inkl. preview plus TaskCreate/TaskUpdate, dazu Typografie-Sperre gegen Halbgeviert-/Geviertstrich und Pfeil) nach .claude/hooks/ ausgerollt und in .claude/settings.json als PreToolUse-Hook (matcher AskUserQuestion|TaskCreate|TaskUpdate) verdrahtet. Zentral aus ~/.claude/hook-templates via Sync-UmlautTriggers.ps1. Bestehende Hooks bleiben unangetastet.\n\nEntscheid: claudecode ADR-2026-06-26-1254.",
          "is_bot": false,
          "headline": "PreToolUse-Surrogat-Sperre für prosa-tragende Tool-Aufrufe verdrahtet",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-26T11:08:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b006ce193d154eadf3d30a739f70bde018245477",
          "body": "…d (F-MK9-08-Sub)\n\nPolymorphic lookups (Customer, custom multi-target) need a target-qualified $expand\nnavigation property per target. The name is case-sensitive and not reliably\nconstructible (ownerid -> owninguser/owningteam, custom lookups carry SchemaName\ncasing), so it is read from OneToManyRel\n[…]\n; metadata-client/EntityTypeInfo mocks updated.\n  Verified live against markant-dev (customerid_account/contact; ownerid special case).\n\nBumps: typegen 0.15.0, devkit 0.7.36, cli 0.9.0. Not published.",
          "is_bot": false,
          "headline": "feat(typegen): generate XxxExpands enum for polymorphic lookup $expan…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-22T06:46:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a43542249f960703a657d8f280e2b65524f1931d",
          "body": "…ation (Runde 10)\n\nhelpers README 'What you get' table was stale across several releases, not just this\none: add clearAppNotification, parentXrm/getWebResourceContext (this round) plus the\npreviously-missing expanded/expandedMany, setAndSubmit, formLookupIdUnsafe/Unsafe,\ngetEnvironmentVariable, isUn\n[…]\n MockControl.setContentWindow; list the now-exported\nTrackedAppNotification type (added to index.ts, return type of getGlobalNotifications).\n\ntesting build+typecheck green; type present in dist .d.ts.",
          "is_bot": false,
          "headline": "docs: bring helpers/testing READMEs current; export TrackedAppNotific…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T21:29:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9f6e5fb74d3694d4f4b30fab584bb791f76f071f",
          "body": "… OE-14)\n\nhelpers 0.14.0:\n- clearAppNotification(id): non-breaking counterpart to addAppNotification,\n  encapsulates the Xrm.App optional-at-runtime cast (FW-4)\n- parentXrm() + getWebResourceContext(): browser-safe HTML WebResource context\n  helpers, replace the window.parent / page-context hand-cas\n[…]\n-4), SchemaName casing read-not-guess (FW-6), WF/system actions have no\n  generated executor (FW-7)\n\nAll 4 gates green (build 7/7, test 14/14 incl. K44-01 scaffold-typecheck,\ntypecheck 9/9, lint 7/7).",
          "is_bot": false,
          "headline": "feat: HTML WebResources first-class + clearAppNotification (Runde 10,…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T21:14:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4bffba7647341f8f8a7a26eeefafa07bfc7228bf",
          "body": "…blocks",
          "is_bot": false,
          "headline": "chore(hooks): umlaut check for all text files; code warns, .md still …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T17:51:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b437443aafb2f8b10b96981a7afe274498b897b",
          "body": "…2, devkit 0.7.34, cli 0.8.20)\n\n- helpers (R46-06): clarify expanded<T> JSDoc - returns null only for an absent/null/array\n  nav property; a present object (even {}) is returned as-is. Doc/.d.ts only, no behaviour change.\n- typegen (R46-07): extract a single shared buildAttributeMemberName() in labe\n[…]\ngacy modernization).\n- cli: propagate typegen 0.14.2 + devkit 0.7.34 (no source change).\n\nR46-03/04/05 were already resolved in d7c7ea4 (Session 46); confirmed by reading the tests.\nNot yet published.",
          "is_bot": false,
          "headline": "fix: R46-02/06/07 doc + refactor batch (helpers 0.13.1, typegen 0.14.…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T10:34:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d7c7ea4cbe76d15d57f1e18d04fe42ae47a2dfa3",
          "body": "… (R46-01)\n\n0.14.0 extended the cached EntityTypeInfo (multiSelectPicklistAttributes) and\nnormalized MultiSelect attributes (Virtual -> MultiSelectPicklist) on load, but\nleft the cache manifest version at '1'. With useCache:true an old cache was\naccepted as compatible, so an unchanged-from-cache ent\n[…]\n05 (form-generator collision guard + SchemaName\nfallback + multi-part SchemaName, metadata-client MultiSelect negative case,\nhelpers expanded empty-object). Propagate to cli 0.8.19. All 4 gates green.",
          "is_bot": false,
          "headline": "fix(typegen): bump CACHE_VERSION to 2 to invalidate pre-0.14.0 caches…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T04:06:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9602d847c59ef083ddc1a4430c3d08ecb485cc80",
          "body": "New tests/scaffold-typecheck.test.ts scaffolds a real project into a temp dir,\nsymlinks @xrmforge/helpers and @types/xrm into a temp node_modules offline (no\nnpm install), and runs tsc --noEmit against the unchanged generated tsconfig,\nexpecting exit 0. Catches type errors in shipped templates (error-handler.ts,\nexample-form.ts, shared/*) that the devkit tsc excludes (templates/** is in its\nexclude). Test-only, no version bump; runs in the existing vitest/CI gate.",
          "is_bot": false,
          "headline": "K44-01: typecheck scaffold templates with a devkit test (Option C)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T03:23:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fc70db084a7437fef42de16052a4974ab7e8c573",
          "body": "helpers 0.13.0: expanded<T>(entity, nav): Partial<T> | null (single-valued) and\nexpandedMany<T>(entity, nav): Partial<T>[] (collection-valued) read an expanded\nnavigation property from a Web API response as a typed object/array. Partial<T>\nis honest about a partial $select inside the expand; replace\n[…]\n\ndevkit 0.7.33: scaffold pins helpers ^0.13.0; AGENT.md documents reading expand\nresults via expanded/expandedMany instead of hand-casting. cli 0.8.18 propagation.\nAll 4 gates green; +7 helpers tests.",
          "is_bot": false,
          "headline": "F-MK9-08: typed $expand readers expanded<T>() / expandedMany<T>()",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T00:46:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6a9219b4a353af57e4bdcfb2b7be46fa5fa57802",
          "body": "…solution\n\nFields/NavigationProperties/FormFields enum members now derive from the attribute\nSchemaName instead of the display label, dropping the order-dependent ordinal\nsuffix. Deterministic, stable and guessable (statecode -> StateCode), matching\npac modelbuilder / XrmDefinitelyTyped. Label stays\n[…]\nedicated cast query so the enum is generated\n(F-MK9-09).\n\ntypegen 0.14.0 (MINOR, breaking output), devkit 0.7.32 (AGENT.md naming note),\ncli 0.8.17 (propagation). All 4 gates green; 462 typegen tests.",
          "is_bot": false,
          "headline": "F-MK9-05/07/09: SchemaName-based Fields enum members + MultiSelect re…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-21T00:40:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "cd4992a32326ec71f369a2086c44a27826bc040c",
          "body": "helpers: addAppNotification gains an optional autoHideMs that schedules\nXrm.App.clearGlobalNotification after the delay (fire-and-forget), replacing\nhand-built transient-notification helpers (account/opportunity duplicated one).\n+2 tests with fake timers. helpers 0.11.0 -> 0.12.0 (MINOR).\ndevkit scaffold pin helpers ^0.12.0 + AGENT.md note; devkit 0.7.30 -> 0.7.31.\ncli 0.8.15 -> 0.8.16 (workspace:* propagation). All 4 gates green.",
          "is_bot": false,
          "headline": "F-MK9-10: addAppNotification autoHideMs (self-clearing banner)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-20T22:44:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0939f48676260c01e593934e2c48b353f69b9683",
          "body": "…template\n\nDecision Option C (app-level banner error surface). error-handler.ts template:\n- wrapCommand is now generic (wrapCommand<TArgs extends unknown[] = []>) so ribbon\n  command handlers declare extra params type-safely instead of ...args: never[]\n  (default [] = backward compatible).\n- new wra\n[…]\ny compiling against @types/xrm + helpers\n(templates are excluded from the devkit tsc). devkit 0.7.29 -> 0.7.30 (template-only\nPATCH), cli 0.8.14 -> 0.8.15 (workspace:* propagation). All 4 gates green.",
          "is_bot": false,
          "headline": "F-MK9-02: typed wrapCommand<TArgs> + new wrapGridCommand in scaffold …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-20T21:14:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "07a799c358c6722fa6dc6a66ac3de94f265304c5",
          "body": "… MockControl.refresh (F-MK9-01), AGENT.md NavigationProperties path + 5 pitfalls (F-MK9-04)",
          "is_bot": false,
          "headline": "Runde 9 framework fixes: tree-shakeable action executors (F-LMA9-01),…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-20T17:12:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f7d36eee5c0a04d43b4eed89d7473f111e06b9eb",
          "body": "… multi-form pattern (OE-13)\n\nReplace the \"per-entity union FormTypeInfo (planned)\" note with a clear statement that\nraw Xrm.FormContext + named constants is the deliberate, supported pattern for both\ncross-entity and single-entity-multi-form scripts. The union type was considered and\nrejected (OE-1\n[…]\nccess to fields the active form may not carry -\nthe false compile-time safety the framework exists to prevent.\n\ndevkit 0.7.27 -> 0.7.28 (template-only), cli 0.8.12 -> 0.8.13 (workspace:* propagation).",
          "is_bot": false,
          "headline": "AGENT.md template: raw FormContext + named constants as the supported…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T17:33:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f9f371f681e335cca6a4dc556e0721a79d35ed0d",
          "body": "…8-N3); devkit 0.7.27 + cli 0.8.12\n\nCommitted-not-published (version > npm so the changed AGENT.md content has its own version, no\nsame-version drift). Publish as doc-addendum or bundle into the next release.\n- devkit 0.7.27: AGENT.md template - cross-entity/cross-form script pattern (raw FormContex\n[…]\nr than per-control (F-R8-N3b / F-LMA7-10)\n- cli 0.8.12: propagate devkit 0.7.27 (no cli source change)\nbuild --force 7/7, test 14/14, typecheck 9/9, lint 7/7; dist/templates/AGENT.md content verified.",
          "is_bot": false,
          "headline": "chore(devkit): AGENT.md cross-form pattern + section-toggle note (F-R…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T15:48:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "09ef77292e23e6c72ee693c6430590401ff61fc2",
          "body": "…ld pins + CHANGELOGs (Runde 8)\n\nBundles with the already-committed cli 0.8.11 + eslint-plugin 0.3.2 (Session 41) for one publish block.\n- helpers 0.11.0 (MINOR): setAndSubmit, formLookupId/Unsafe, isUnsavedRecord, getEnvironmentVariable\n- testing 0.5.0 (MINOR): online.execute override + OptionSet/v\n[…]\nit 0.7.26 (PATCH): scaffold pins helpers ^0.11.0 / testing ^0.5.0 (0.x caret) + AGENT.md Runde-8 docs\nbuild --force 7/7, test 14/14, typecheck 9/9, lint 7/7; dist content verified per Stolperfalle 17.",
          "is_bot": false,
          "headline": "chore(release): helpers 0.11.0, testing 0.5.0, devkit 0.7.26 + scaffo…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T15:21:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "77ba9cf9a6a759a57d9474f71f6e94876b83a745",
          "body": "…ntrol(string) pitfall\n\n- Packages line + NEVER block: setAndSubmit (on-form), formLookupIdUnsafe/Unsafe, getEnvironmentVariable, isUnsavedRecord\n- Section 5b: getAttribute/getControl on lookups need the BLANK name (form-level FormFieldsEnum / NavigationProperties), never the entity Fields _value-fo\n[…]\nped $context fails (no string overload); use raw ctx.getFormContext().getControl(name) (F-R8-N5)\n\nScaffold pin bumps (helpers ^0.11.0 / testing ^0.5.0) + version bumps deferred to the bundled release.",
          "is_bot": false,
          "headline": "docs(devkit/AGENT.md): Runde-8 helpers + F-MK8-05 lookup rule + getCo…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T15:16:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7f009b82f5f3913f091632eaa31e9e184e131951",
          "body": "…k methods (Runde 8 gaps)\n\nRemaining mock gaps the .1 testing release did not close (Runde 8):\n- setupXrmMock webApiOverrides.execute / executeMultiple: the default online.execute returns a 204 (no body), making Custom API executors that call response.json() throw. Tests can now inject a JSON Respon\n[…]\naultView (lookup views), setFilterXml/getFilterXml (subgrid, not in @types/xrm).\n- MockAttribute: getOptions/setOptions (OptionSet option list seed).\n\n+6 tests (142 total). build/typecheck/lint green.",
          "is_bot": false,
          "headline": "feat(testing): online.execute override + OptionSet/view/filterXml moc…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T15:04:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bd8b9dfce65e8a220bd80fe52510eee2535c3008",
          "body": "…tEnvironmentVariable (Runde 8 gaps)\n\nGenuine framework gaps surfaced by the Runde 8 showcases (survive the .1 republish):\n- setAndSubmit(attr, value): on-form set + SubmitMode.Always one-liner, type-safe (F-LMA8-N1, the dominant idiom, 200+ call-sites). Explicit opt-in, does not change setValue sem\n[…]\nentVariableCache: cached env-var reader, default fallback, OData-escaped, errors propagate (F-MK8-N4a)\n\n+18 tests (107 total). build/typecheck/lint green. Version bump deferred to the bundled release.",
          "is_bot": false,
          "headline": "feat(helpers): setAndSubmit, formLookupId/Unsafe, isUnsavedRecord, ge…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T15:00:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f38958755a35ab864e2b373cb2740b37a78d42fc",
          "body": "…sion from package.json\n\n- cli README: init --force skips existing files, never overwrites (matched\n  to xrmforge init --help). Docs-only patch bump 0.8.10 -> 0.8.11.\n- eslint-plugin: plugin.meta.version now read from package.json at load time\n  instead of a hardcoded literal (was stale at 0.3.0). Test asserts against\n  package.json so it cannot drift again. Patch bump 0.3.1 -> 0.3.2.\n- Not published (bundle with the next framework release).",
          "is_bot": false,
          "headline": "Fix README init --force wording (cli) + derive eslint-plugin meta.ver…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T14:46:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "723ea4d28ef8b0187c9ccc51dcc116280f309262",
          "body": "Fix the outdated package table and roadmap in README.md/README.de.md: drop\nthe drifting version numbers, move \"xrmforge init\" from Planned to Shipped,\nand remove the fixed test count. Patch-bump all seven packages so the new\nper-package READMEs become visible on npm at the next publish (npm renders\n\n[…]\ni 0.8.10, helpers 0.10.1, webapi 0.1.2, testing 0.4.1,\ndevkit 0.7.25, eslint-plugin 0.3.1. Docs only, no source change; build 7/7\nand test 14/14 green. NOT yet published (interactive publish pending).",
          "is_bot": false,
          "headline": "Prepare docs release: root README cleanup + patch bumps for all packages",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T10:10:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f0a02ed78ee346ee220d4133b5c83ec44b22de07",
          "body": "npm showed no README on any @xrmforge/* package page; the detailed root\nREADME belongs to the private root package and is never published. Add a\ndedicated README per package (cli, typegen, helpers, webapi, testing,\ndevkit, eslint-plugin) with usage, API overview, and cross-links.\n\nVerified via npm pack --dry-run that README.md ships in every tarball\n(npm always includes README regardless of the files field), so no\npackage.json change is needed.",
          "is_bot": false,
          "headline": "Add per-package READMEs for all seven npm packages",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-19T08:38:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3502ba37840f7cf1bf3e6e0ecb6ef42120e5866e",
          "body": "…ion base (Runde 7 Prio 6/7)\n\n- form-mapping.json records per-form `fields` + `isMain` marker, built from structured\n  per-form metadata during generation instead of regex over the output (F-MAR7-04)\n- typed form sections extend Xrm.Collection.ItemCollection<Section> so get(index)/get()/\n  forEach/g\n[…]\nce) (F-LMA7-11)\n- README EN/DE: reproducible-CLI-version tip (npx caches @latest; pin or use local binary) (F-LMA7-02)\n- bumps: typegen 0.13.0 (MINOR), devkit 0.7.24, cli 0.8.9; per-package CHANGELOGs",
          "is_bot": false,
          "headline": "feat(typegen): form-mapping fields/isMain + typed-section ItemCollect…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T16:46:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f848e8467f6b9a0825f99fa42f46073bb2bfde09",
          "body": "…fication (F-MAR7-03, F-LMA7-07/09)\n\nHoist helpers the showcase sessions hand-rolled per form into @xrmforge/helpers: parseMultiSelect(value, emptyAsNull?) normalizes MultiSelect OptionSet shapes (comma string / number[] / number / null) to number[] (or null), dropping empty parts before Number() (n\n[…]\nNEVER entries against hand-rolled equivalents + @odata.bind note. Scaffold helpers pin ^0.9.0 -> ^0.10.0. Bumps: helpers 0.10.0 (minor), devkit 0.7.23, cli 0.8.8. All 4 gates green (helpers 89 tests).",
          "is_bot": false,
          "headline": "feat(helpers): parseMultiSelect, clear/setUnsafeAndSubmit, addAppNoti…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T13:03:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e2905e56aefe8ddfb828453d139aca0d1ede8a19",
          "body": "Bound MS-standard operations (SynchronizePhoneNumbers, PredictResult) share an exported name across entity action modules, so the barrel's export * from './actions/...' collided with TS2308 on an unfiltered 'generate --actions' run (the --actions-filter workaround only masked it). Actions/functions \n[…]\non clarified (entities + forms via export *; OptionSets/Fields/Actions imported directly, name-collision-safe).\n\nBumps: typegen 0.12.2, devkit 0.7.22, cli 0.8.7. All 4 gates green (typegen 453 tests).",
          "is_bot": false,
          "headline": "fix(typegen): stop export-star of action modules in barrel (F-LMA7-01)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T12:02:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7fe8ccf62693f05bb21237e1558769de2683ff89",
          "body": "createFormMock gains a formType option (seed getFormType, test Create-only paths). MockAttribute gains getText/setText + getPrecision/setPrecision. MockEntity gains addOnSave/removeOnSave + FormMock.fireOnSave(saveMode). setupXrmMock exposes userSettings.roles as a real ItemCollection (get/forEach/g\n[…]\nst.\n\nAGENT.md template testing section documents the new helpers. Scaffold testing pin ^0.3.0 -> ^0.4.0.\n\nBumps: testing 0.4.0 (minor), devkit 0.7.21, cli 0.8.6. All 4 gates green (testing 136 tests).",
          "is_bot": false,
          "headline": "feat(testing): close complex-form mock gaps (F-MAR7-02)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T11:42:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bd6ae8f91a7e3fcb068fe2a85027e5061a0e509c",
          "body": "…MAR7-01)\n\nA void action executor (createBoundAction/createUnboundAction without a TResult type arg) was typed Promise<Response> but at runtime returned response.json() (200) or the raw Response (204), while execute() already throws on non-2xx. Following the type with if (!resp.ok) crashed at runtim\n[…]\n case; matching NEVER entry, subagent-handoff note, corrected before/after example. Scaffold helpers pin ^0.8.0 -> ^0.9.0.\n\nBumps: helpers 0.9.0 (minor, type-level breaking), devkit 0.7.20, cli 0.8.5.",
          "is_bot": false,
          "headline": "fix(helpers): void Custom API executors return void, not Response (F-…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T11:13:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4e6d4347fb860693a2908fdf8648367569b02052",
          "body": "…7-06)\n\n$unsafe(Lookup) takes an attribute logical name -> use XxxNavigationProperties\n(blank), not XxxFields (_value-form, resolves to null at runtime). Non-lookup\noff-form fields still use XxxFields. Doc-only: a static gate cannot distinguish\nlookup from non-lookup at the $unsafe call site without false positives. Part of\nthe unpublished devkit 0.7.19 / cli 0.8.4.",
          "is_bot": false,
          "headline": "docs(devkit): cover $unsafe on off-form lookups in AGENT.md 5b (F-LMA…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T06:20:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "23d321d8de9d7f4edca155ce65a977dd07813243",
          "body": "AGENT.md section 5b: the XxxFields lookup enum is already _value-form (use in\n$select/$filter), XxxNavigationProperties is blank (use for parseLookup/$expand/\n@odata.bind). Double-wrapping _${XxxFields.X}_value or passing a Fields value to\nparseLookup compiles green but breaks at runtime (OData 400 \n[…]\ncaffold.test.ts asserts the new section ships.\n\ndevkit 0.7.18 -> 0.7.19 (template-only), cli 0.8.3 -> 0.8.4 (propagation, pulls\ndevkit 0.7.19 via workspace:*, no cli source change). All 4 gates green.",
          "is_bot": false,
          "headline": "feat(devkit): scaffold lookup convention guidance + gate (F-LMA7-05)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-18T06:18:28Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c75dc12ab1a5a8a1accb29837023f1da055e18b4",
          "body": "GridControl.setFilterXml is missing from the typings (cast the control), and\nXrm.App global-notification `level` is an XrmEnum (not available at runtime;\npass the numeric value). Both hit in the markant showcase run. Folded into the\nunpublished devkit 0.7.18 entry.",
          "is_bot": false,
          "headline": "Add two @types/xrm gaps to AGENT.md pitfalls list (Runde-6 follow-on)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-17T10:03:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "44df3e319b906078fcb134c07b7e63fb5ad6bd15",
          "body": "K32-06 follow-on: withProgress no longer opens its own error dialog (the\nhandler wrapper wrapHandler/wrapCommand owns the single error UI). Updated\nthe README usage comments and the \"Progress Indicator\" section in both\nlanguage versions accordingly.",
          "is_bot": false,
          "headline": "Fix stale withProgress error-dialog claim in README (EN+DE)",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-17T10:01:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c24606e9c24d6cd66910b431bd334312237e36b2",
          "body": "- typegen [0.12.1]: escape apostrophes in emitted FormXML names (new\n  generators/string-escape.ts; section/tab names, subgrid/quickview ids).\n  A section named \"note's information\" no longer breaks the generated TS.\n- helpers [0.8.0]: add isFormType(ctx, FormType) (TS2367-safe form-type\n  equality)\n[…]\nard uses isFormType; scaffold pins\n  helpers ^0.8.0 / testing ^0.3.0.\n- cli [0.8.3]: init next-steps print --output ./generated.\n\nAll gates green (build 7/7, test 14 tasks, lint 7/7). Publish pending.",
          "is_bot": false,
          "headline": "Fix Runde-6 framework findings K32-02 to K32-07",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-17T08:40:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2896c94e55942951b850743f96e7cf7799bccdfe",
          "body": "…32-01)\n\nThe AGENT.md scaffold template now teaches callCloudFlow (helpers 0.7.0) as the canonical way to call a Power Automate cloud flow (HTTP-request trigger) from a form script: helpers package blurb, a NEVER rule against hand-rolled fetch/XMLHttpRequest for flow calls, a 'Cloud Flow Call' befor\n[…]\ncklog C, found in the Session 32 audit. devkit 0.7.16 -> 0.7.17 (template-only PATCH), cli 0.8.1 -> 0.8.2 (propagation, pulls devkit 0.7.17 via workspace:*). All 4 gates green, dist template verified.",
          "is_bot": false,
          "headline": "feat(devkit): document callCloudFlow in AGENT.md scaffold template (K…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-16T11:13:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "224127053fee06a58cf7de8014fc3900a05ad45a",
          "body": "…CloudFlow)\n\nA 0.x caret never crosses the 0.6/0.7 boundary, so fresh init projects pulled helpers 0.6.x without callCloudFlow. Bumps: devkit 0.7.15 -> 0.7.16 (scaffold pin), cli 0.8.0 -> 0.8.1 (propagation so npx xrmforge init ships the new devkit).",
          "is_bot": false,
          "headline": "chore(devkit): bump scaffold helpers pin ^0.6.1 -> ^0.7.0 (reach call…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-16T10:39:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8701023ec4fa6e0977be18f0598c838a714882cb",
          "body": "…r calls (Backlog C)\n\nBrowser-safe callCloudFlow<TReq, TRes>(triggerUrl, body?, options?): POSTs the body as JSON, returns parsed JSON / raw text / undefined (204), throws on non-2xx with status + body. Covers the direct HTTP-trigger case (Custom API / proxied flows stay with createUnboundAction). Trigger URL (SAS) is a parameter, never hard-coded; compose with withProgress. Zero Node deps.\n\n7 fetch-mocked tests. Bump: helpers 0.6.3 -> 0.7.0 (minor).",
          "is_bot": false,
          "headline": "feat(helpers): add callCloudFlow for typed Power Automate HTTP-trigge…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-16T10:25:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e4ba546c82b3bd28f890b10b3db15b5334cef737",
          "body": "…ufe 2)\n\ngenerate now auto-loads a local ./.env (via dotenv, without overriding real env vars) and, in an interactive terminal, prompts for any still-missing connection/credential value (hidden secret input). Full precedence per value: CLI flag > env var > ./.env > xrmforge.config.json > interactive\n[…]\nt scaffold .gitignore now ignores .env/.env.local; scaffold cli pin ^0.7.0 -> ^0.8.0.\n\nNew cli dependency: dotenv (zero-dependency). Bumps: cli 0.7.0 -> 0.8.0 (minor), devkit 0.7.14 -> 0.7.15 (patch).",
          "is_bot": false,
          "headline": "feat(cli): auto-load .env and interactive credential prompt (OE-12 St…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-16T09:47:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bb5098913d72f41bbec62ca84f14c7034921bedb",
          "body": "…E-12 Stufe 1)\n\ngenerate now reads XRMFORGE_URL/TENANT_ID/CLIENT_ID/CLIENT_SECRET from the environment (XRMFORGE_TOKEN already supported). Precedence per value: CLI flag > env var > xrmforge.config.json. Fixes K30-05 (the CLI advised XRMFORGE_CLIENT_SECRET but never read it). buildAuthConfig is now \n[…]\nv block, so the secret never reaches the runner's process list; scaffold cli pin ^0.6.0 -> ^0.7.0 (env reading needs cli >= 0.7.0).\n\nBumps: cli 0.6.2 -> 0.7.0 (minor), devkit 0.7.13 -> 0.7.14 (patch).",
          "is_bot": false,
          "headline": "feat(cli): resolve connection/credentials from XRMFORGE_* env vars (O…",
          "author_name": "Jürgen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-16T07:57:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dcc323dcd9780bfadc6aa498a801eaed1b9a401b",
          "body": "…erschien nie)\n\nDer PostToolUse-Hook gab hookSpecificOutput ohne hookEventName aus; Claude Code\nverwarf den additionalContext daher still. Aus dem zentralen Template gesynct,\nauf macOS live verifiziert.",
          "is_bot": false,
          "headline": "check-umlaute.py: hookEventName PostToolUse ergänzen (Schreibwarnung …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-15T05:24:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55ae911866a7a51988c6c5a6595a45a71ef29640",
          "body": "…GELOG\n\nThe manual publishes left several per-package CHANGELOGs stale (only the workspace\nchangelog tracked them). Reconstruct from the workspace changelogs and git history:\n- helpers: 0.6.2, 0.6.3\n- typegen: 0.10.2 (git-only), 0.11.0, 0.11.1, 0.12.0\n- cli: 0.4.21 (git-only), 0.5.0, 0.5.1, 0.5.2 (git-only), 0.6.0, 0.6.1, 0.6.2\n- eslint-plugin: new CHANGELOG (0.1.0 through 0.3.0)\n\ngit-only interims are marked as such. Docs only, not in the npm tarballs\n(files: [\"dist\"]), no version bump.",
          "is_bot": false,
          "headline": "docs: backfill helpers/typegen/cli CHANGELOGs, add eslint-plugin CHAN…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T06:19:37Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7aeb90eff37c6ae499a8e5dcc37d936e08e33276",
          "body": "The manual publishes since 0.7.7 did not update the per-package CHANGELOG\n(only the workspace changelog tracked them). Reconstruct entries 0.7.8 through\n0.7.13 from the workspace changelogs, each with its finding reference. 0.7.8 is\nmarked git-only (never published, folded into 0.7.9). Docs only, not in the npm\ntarball (files: [\"dist\"]), no version bump.",
          "is_bot": false,
          "headline": "docs: backfill devkit CHANGELOG 0.7.8-0.7.13",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T06:07:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a0639b08fdc814fc6fa43da7fdf5c4aa8065eef3",
          "body": "The typedForm API exposes controls.fieldName (a getControl proxy), not\n$control(name). $control was replaced by the controls proxy (helpers 0.5.0,\nfinding K25-03); a form.$control(...) call throws at runtime. Align the\nGET-trap description in docs/architecture/13-helpers.md (EN) and\ndocs/architektur/13-helpers.md (DE). Docs only, no version bump.",
          "is_bot": false,
          "headline": "docs: correct typedForm GET-trap to controls.fieldName",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T05:55:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ea6217b34b270dce1b451e9b14896154b85a42af",
          "body": "cli pulls @xrmforge/devkit and @xrmforge/typegen via workspace:*. Republishing\ncli freezes the new devkit 0.7.13 (gate consolidation, F28-01 AGENT.md, K29-01\nexample-form template, CI validate step) and typegen 0.12.0 into the versions\nthat `npx xrmforge init` installs. No cli source change.",
          "is_bot": false,
          "headline": "Bump cli 0.6.1 -> 0.6.2 to propagate devkit 0.7.13",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T01:19:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2b81c7fada1d16cb01d44b6b12260d4212fcce98",
          "body": "Both scaffolded CI templates (GitHub Actions, Azure Pipelines) ran\ntsc + vitest + build but neither eslint nor validate-form.mjs, so the lint\nand pattern-compliance gates only ran locally, never in a scaffolded project's\nCI. Replaced the standalone tsc step with `npm run validate`, which runs\ntsc + \n[…]\n 28 consolidation) - no redundant tsc run.\n\nscaffold.test.ts now asserts the GitHub template contains 'npm run validate'.\nGoes into devkit 0.7.13 (not yet published). All 4 gates green, dist verified.",
          "is_bot": false,
          "headline": "Run validate (lint + pattern gates) in scaffold CI templates",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T01:06:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "73028d7d4d09342defb15e70424ebe3d34251cbb",
          "body": "Five `weiss`, one `schliesst`, one `Schliessen` and one `fuer` in German\ncode-block comments and a test description string used ASCII surrogates\ninstead of real umlauts. They sit inside typescript code fences, which the\numlaut commit hook skips by design, so they were not auto-caught. Focused\ncleanup, no functional change.",
          "is_bot": false,
          "headline": "Fix remaining ASCII surrogates in README.de.md code-block comments",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T00:52:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "acf7f447b13b58d5e75691678c381d1a6db5baf2",
          "body": "…TypeInfo>\n\nSession 28's F28-01 fix corrected AGENT.md, helpers @example and the\ntypes-xrm-extension skill, but missed two derived artifacts that still showed\nthe fragile bare-interface pattern:\n\n- devkit scaffold template example-form.ts: the TODO taught\n  typedForm<ExampleForm> (bare interface -> \n[…]\n in\nREADME.de.md, flagged by the umlaut hook.\n\nNo version bump: devkit 0.7.13 is not yet published (the dist template is\nrebuilt before publish), READMEs are not in the npm tarball. All 4 gates green.",
          "is_bot": false,
          "headline": "Fix F28-01 remnants: example-form template + README use typedForm<...…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-14T00:38:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ae942337fe612e9fc0b670785fcefee5ffce2246",
          "body": "AGENT.md and the typedForm @example documented typedForm<FormInterface>, the fragile\npattern: ExtractFields falls back to overload inference that resolves to `never` across\npackage boundaries (typedForm-TS59). The reliable pattern is typedForm<FormTypeInfo> (the\ngenerated TypeInfo bundles the field/\n[…]\n 0.6.3 (@example JSDoc change)\n\nAGENT.md ships in devkit 0.7.13 (already bumped). All gates green. Publish pending\n(interactive): devkit 0.7.13 + helpers 0.6.3 + a cli bump to propagate the templates.",
          "is_bot": false,
          "headline": "Fix F28-01: AGENT.md + helpers @example use typedForm<FormTypeInfo>",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T23:45:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4c42cf4be54c6e94caa44a2cdfe9b7245e9ff888",
          "body": "The checked-in example used the obsolete declare-namespace/.d.ts output and\ngetAttribute chains. Bring it to the current pattern (K22-04).\n\n- generate-from-fixture.ts: drop the obsolete namespace/namespacePrefix generator\n  options (only labelConfig remains), write .ts instead of .d.ts, emit a flat \n[…]\n (examples is not a package).\n\nNote: surfaced that AGENT.md documents typedForm<FormInterface>, which fails type\nextraction across package boundaries (use typedForm<FormTypeInfo>). Tracked separately.",
          "is_bot": false,
          "headline": "Modernize examples/account-form-demo to flat ESM + typedForm",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T23:32:02Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c22525a6bbecbc36f99ba925bdc5f08e584aa065",
          "body": "self-check.sh was orphaned: no package.json script, not referenced by AGENT.md,\nnot run in CI. validate-form.mjs is a superset of its pattern checks plus tsc and\neslint, and runs cross-platform (node, not bash). Retire self-check.sh after porting\nits three unique checks so no coverage is lost.\n\n- va\n[…]\n8 -> 17\n- delete the self-check.sh template (dist follows via copy-templates)\n- devkit 0.7.12 -> 0.7.13\n\nAll four gates green; dist verified (self-check.sh gone, validate-form.mjs has the new checks).",
          "is_bot": false,
          "headline": "Consolidate scaffold quality gates: validate-form.mjs as single source",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T23:03:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0756d0a0f839ecd64d3a2253af60c01c4052626c",
          "body": "PERSIST_PATH_RE von den Markant-Defaults (skills, architekturen, memory) auf\ndocs, skills, memory umgestellt, damit der Recherche-Block-Zähler nach einem\nWissens-Write in den docs/-Baum korrekt zurücksetzt.",
          "is_bot": false,
          "headline": "Wissens-Persistenz-Hook: Pfad-Tokens an die Doku-Struktur anpassen",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T22:39:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1271544a3cc16ab30df92ef179b3067e871a5300",
          "body": "…sistenz\n\n- Umlaut-Kette in .githooks (commit-msg, pre-commit, umlaut_check_lib.py) und\n  PostToolUse-Schreibwarnung check-umlaute.py, ausgerollt aus der zentralen\n  Trigger-Liste.\n- UserPromptSubmit check-kontextlast.py: Warnung bei 40/60/75 Prozent Kontextfüllung.\n- UserPromptSubmit check-wissens-persistenz.py: Erinnerung, Wissen wegzuschreiben.\n- SessionStart/-End Git-Sync und core.hooksPath-Selbstaktivierung.",
          "is_bot": false,
          "headline": "Claude-Code-Hooks einrichten: Umlaut-Schutz, Kontextlast, Wissens-Per…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T17:41:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1c3737bb34bf1d20ebad5a8c96a177ae24725e98",
          "body": "K23-01 cleanup: generators emit flat ES modules (.ts), but several JSDoc/header comments still said .d.ts. Corrected in entity-generator, action-generator (module header + section + combine note), orchestrator, orchestrator/types, file-writer. Comment-only, no API/behavior/output change, no version bump. All 4 gates green.",
          "is_bot": false,
          "headline": "docs(typegen): correct stale .d.ts comments to flat ESM (.ts)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T17:06:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "25acc6b805b4bfa25bdcce6f46328279ace768ae",
          "body": "…control refs (K25-03), republish helpers JSDoc\n\ndevkit 0.7.11->0.7.12: scaffold cli pin ^0.5.0->^0.6.0 (0.x caret never resolves 0.6.0); example-form template comment controls proxy. helpers 0.6.1->0.6.2: republish typed-form JSDoc fix (ed49a2e) + test comment/name controls. cli 0.6.0->0.6.1: pull devkit 0.7.12 + typegen 0.12.0 via workspace:*.",
          "is_bot": false,
          "headline": "fix(devkit,helpers): bump cli scaffold pin to ^0.6.0 (K26-01), drop $…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T14:39:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ed49a2e11414d4bfa19424313845fe69169acb03",
          "body": null,
          "is_bot": false,
          "headline": "docs(helpers): correct typed-form JSDoc (\\ -> controls proxy)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T13:59:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1a29aa4a681853f3afc0c0e420c6c21a3d48c808",
          "body": "Per review: inactive forms should get no interface regardless of type. The\nactivation filter now sits outside the type group:\n  (type eq 2 or type eq 7) and formactivationstate eq 1\nso it applies to Main forms as well, not just Quick Create. Consistent and\ncorrect (inactive Main forms are leftovers no app surfaces). Part of the\nunpublished typegen 0.12.0.",
          "is_bot": false,
          "headline": "refactor(typegen): apply formactivationstate filter to Main forms too",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T13:34:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8f9a6d57ec38dd47739256053a850c24072d4e74",
          "body": "Quick Create forms (systemform type=7) previously had no generated interface.\ntypegen now fetches and generates them, data-driven (no opt-in flag):\n\n- metadata: SystemFormMetadata/ParsedForm carry the form type; getMainForms ->\n  getForms fetches Main (type 2) plus ACTIVE Quick Create (type 7 and\n  \n[…]\ne duplicate disambiguation.\n- tests: getForms (type carried through, QC included, query filter), QC naming +\n  disambiguation. typegen 441 -> 445 tests.\n\ntypegen 0.11.1 -> 0.12.0 (minor, new feature).",
          "is_bot": false,
          "headline": "feat(typegen): generate active Quick Create forms (type 7)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T13:30:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8a117b08e05daa6691a2ccbbd4ea4e0753f490ad",
          "body": "…LMA-02)\n\n- config.ts: add actions/actionsFilter to XrmForgeConfig and mergeWithCliOptions\n  (CLI takes precedence). Custom API scope is now check-in-able so `generate`/\n  `--check` reproduce without reconstructed CLI options.\n- generate.ts: drop the Commander default `false` on --actions so a confi\n[…]\naults to false, CLI-only behavior unchanged).\n- new config.test.ts: 6 unit tests for mergeWithCliOptions (actions/actionsFilter\n  precedence + regression). cli 0.5.x -> 0.6.0 (additive config fields).",
          "is_bot": false,
          "headline": "feat(cli): read actions/actionsFilter from xrmforge.config.json (F23-…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T13:08:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "91000293b1e88807dd02e1acb6eb6813e64ed1bb",
          "body": "…self-check.sh\n\n- eslint-plugin 0.3.0: new AST rules no-raw-field-strings, no-raw-entity-names,\n  no-raw-select (in recommended as error). 16 new RuleTester cases (53 total).\n- devkit 0.7.11: CI templates (github-actions/azure-pipelines) drop the\n  non-existent `--from-config` flag and use explicit \n[…]\n  pin ^0.2.1 -> ^0.3.0.\n- cli 0.5.2: pulls devkit 0.7.11 via workspace:* so `xrmforge init` ships the\n  fixed templates.\n\nAlso bundles the Session 24 devkit template fixes (K24-01/K23-02) for release.",
          "is_bot": false,
          "headline": "feat(eslint-plugin): add no-raw rules; fix scaffold CI templates and …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T12:49:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "449265af9b8cd80f54c2f279a9630ddcbc2ecc1a",
          "body": "K24-01: AGENT.md withProgress example used a non-existent signature\nwithProgress(<Promise>, {title}); corrected to withProgress(message, () => op)\nmatching helpers/action-runtime.ts (now also pickLang-compliant).\nK23-02: example-form.test.ts scaffold template showed an outdated createFormMock\nexampl\n[…]\ntest files), typecheck 9/9, lint 7/7. No version bump: comment/dead-code\nchanges are internal; the devkit template fixes (AGENT.md, example-form.test.ts)\nride the next release before showcase round 6.",
          "is_bot": false,
          "headline": "Doc/comment drift cleanup + remove dead typegen runtime duplicates",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T11:07:37Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a734331ed2253321f8eb2fd204549e591b512a0f",
          "body": "checkGeneratedFile and the write-skip comparison now normalize line endings\nbefore the byte comparison. On Windows with git core.autocrlf=true and no\n.gitattributes, generated files are checked out as CRLF while typegen writes\nLF, which made --check report every file as changed (false drift, exit 2)\n[…]\n generated/** (and\n*.ts/*.mjs/*.json) to eol=lf for clean diffs. README --check section (EN+DE)\nupdated to note that line endings are no longer drift.\n\nBumps: typegen 0.11.1, devkit 0.7.10, cli 0.5.1.",
          "is_bot": false,
          "headline": "fix(typegen): make generate --check line-ending insensitive (CRLF/LF)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-13T08:47:40Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e56aeebf030c0ae48b7eb987cde2d6f8a722853d",
          "body": "…in the minor)",
          "is_bot": false,
          "headline": "fix(devkit): scaffold cli pin follows cli 0.5.0 (0.x caret stays with…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T08:08:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0b46302e48e22026f731ebb3618b318a618b3bec",
          "body": null,
          "is_bot": false,
          "headline": "chore: release typegen 0.11.0, devkit 0.7.9, cli 0.5.0",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T08:08:11Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "42cf5b8259b52c0d58a4a7544d290fbec75ac72a",
          "body": "xrmforge generate --check runs the full generation in-memory and compares the result byte-by-byte against the output directory without any write access (no output files, no cache reads or updates). Findings are reported per category (Entities, Fields, Forms, OptionSets, Actions) with three drift cla\n[…]\n On fetch failures or abort no check result is produced (error, not drift). Docs: README flag table + drift detection section (EN/DE), AGENT.md drift check section. Closes OE-11 (Option B, Release 2).",
          "is_bot": false,
          "headline": "feat: drift check mode for generate (--check, exit 0/1/2)",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T08:08:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "313e6e6c23ebe5e08c767541219b608af3c0cbb7",
          "body": null,
          "is_bot": false,
          "headline": "chore: release typegen 0.10.2, devkit 0.7.8, cli 0.4.21",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T07:59:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "762c7266aaead932e35df044d40182e6040ff5e5",
          "body": "…sion pins\n\nexample-form.ts and example-form.test.ts imported symbols only used in commented examples, so freshly scaffolded projects (and this repo's lint) failed no-unused-vars. The scaffolded package.json pinned helpers ^0.3.0 and cli ^0.4.11; 0.x caret ranges only allow the same minor, so new projects installed outdated helpers without typedForm fixes, controls proxy, and auto-submit. Pins updated to current minors.",
          "is_bot": false,
          "headline": "fix(devkit): scaffold template lint errors and outdated @xrmforge ver…",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T07:59:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9efe86a6c7a752bf8c96a8e98d436e60c4e10cc4",
          "body": "…response properties\n\nOData returns customapi rows in server order (no orderby), so parameter order in generated action modules followed createdon and shifted whenever parameters were recreated. getCustomApis() now sorts APIs, request parameters, and response properties alphabetically by uniquename \n[…]\nput for all generators. Note: the first generate run after this release produces a one-time reordering diff in consumer projects.\n\nPrerequisite for drift detection (generate --check, OE-11 Release 1).",
          "is_bot": false,
          "headline": "fix(typegen): deterministic ordering of Custom APIs, parameters, and …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-06-12T07:58:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c40b3edaacd3dd22bea585d13b175555b21c99e0",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: release helpers 0.6.1, devkit 0.7.7, cli 0.4.20",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-04-09T18:48:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b831f400dcaa8cb0a06bd65f62bb74d16981c655",
          "body": "…at runtime)\n\nesbuild does not resolve const enums from .d.ts files. XrmEnum.FormType\nfrom @types/xrm is a const enum that only exists at compile time. Using\nit in code causes \"XrmEnum is not defined\" at runtime in D365.\n\nAdded FormType enum to @xrmforge/helpers with same values (Undefined=0,\nCreate=1, Update=2, ReadOnly=3, Disabled=4, BulkEdit=6).\n\nAGENT.md: NEVER use XrmEnum.FormType, use FormType from @xrmforge/helpers.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: FormType const enum in helpers (XrmEnum.FormType does NOT exist …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-04-09T18:48:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c95daa9dfbe89885602c8074fb76cd0a02a1d27a",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: release helpers 0.6.0, typegen 0.10.1, cli 0.4.19",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-04-09T17:22:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3d70b0a69aaf8a138b4e419877c8319c92dd6095",
          "body": "…setSubmitMode)\n\ntypedForm proxy now wraps every attribute with a Proxy that intercepts\nsetValue() and automatically calls setSubmitMode('always') afterwards.\n\nD365 AutoSave only submits \"dirty\" fields. Programmatically set values\nvia setValue() are NOT marked dirty by default, causing silent data l\n[…]\ndeveloper: form.name.setValue('X') handles everything.\nNo more paired setValue/setSubmitMode calls. No more data loss on AutoSave.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(helpers): typedForm auto-submits on setValue (no more forgotten …",
          "author_name": "Juergen Beck",
          "author_login": "juergenbeck",
          "committed_at": "2026-04-09T17:21:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        }
      ],
      "releases_count": 0,
      "commits_last_year": 250,
      "latest_release_at": null,
      "latest_release_tag": null,
      "releases_from_tags": false,
      "days_since_last_push": 3,
      "active_weeks_last_year": 10,
      "days_since_latest_release": null,
      "mean_days_between_releases": null
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": false,
      "has_contributing": true,
      "health_percentage": 57,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "@xrmforge/cli",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "dataverse",
            "cli",
            "code-generation"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/cli",
          "is_deprecated": false,
          "latest_version": "0.10.8",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 60,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 2339,
          "first_published_at": "2026-04-01T06:12:21.279000Z",
          "latest_published_at": "2026-07-21T19:48:22.263000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 8
        },
        {
          "name": "@xrmforge/devkit",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "dataverse",
            "esbuild",
            "web-resources",
            "iife"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/devkit",
          "is_deprecated": false,
          "latest_version": "0.10.2",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 57,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 2285,
          "first_published_at": "2026-04-03T19:17:34.264000Z",
          "latest_published_at": "2026-07-21T19:48:22.406000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 8
        },
        {
          "name": "@xrmforge/webapi",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "webapi",
            "dataverse",
            "odata",
            "query-builder"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/webapi",
          "is_deprecated": false,
          "latest_version": "0.1.2",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 3,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 30,
          "first_published_at": "2026-04-02T20:27:15.419000Z",
          "latest_published_at": "2026-06-19T13:16:06.614000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 41
        },
        {
          "name": "@xrmforge/helpers",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "dataverse",
            "web-resources",
            "helpers"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/helpers",
          "is_deprecated": false,
          "latest_version": "0.17.0",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 27,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 831,
          "first_published_at": "2026-04-04T13:56:34.024000Z",
          "latest_published_at": "2026-07-19T01:17:24.475000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 11
        },
        {
          "name": "@xrmforge/testing",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "testing",
            "mock",
            "form-scripts",
            "dataverse"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/testing",
          "is_deprecated": false,
          "latest_version": "0.8.0",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 14,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 399,
          "first_published_at": "2026-04-02T10:50:03.103000Z",
          "latest_published_at": "2026-07-05T22:49:16.962000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 24
        },
        {
          "name": "@xrmforge/typegen",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "typescript",
            "xrm",
            "dataverse",
            "type-generator",
            "declaration-files"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/typegen",
          "is_deprecated": false,
          "latest_version": "0.20.0",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 37,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 1284,
          "first_published_at": "2026-04-01T06:09:41.478000Z",
          "latest_published_at": "2026-07-19T01:17:24.830000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 11
        },
        {
          "name": "@xrmforge/eslint-plugin",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "eslint",
            "eslint-plugin",
            "xrm",
            "dataverse",
            "form-scripting"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/eslint-plugin",
          "is_deprecated": false,
          "latest_version": "0.3.2",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 6,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 132,
          "first_published_at": "2026-04-03T20:49:08.757000Z",
          "latest_published_at": "2026-06-19T15:27:52.823000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 40
        },
        {
          "name": "@xrmforge/dataverse-core",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "dynamics-365",
            "dataverse",
            "odata",
            "web-api",
            "typescript",
            "fetch"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@xrmforge/dataverse-core",
          "is_deprecated": false,
          "latest_version": "0.1.0",
          "repository_url": "https://github.com/juergenbeck/XrmForge",
          "versions_count": 1,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 192,
          "first_published_at": "2026-07-05T22:49:05.132000Z",
          "latest_published_at": "2026-07-05T22:49:05.428000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 24
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 1,
      "watchers": 0,
      "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": [
        "examples"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "examples/account-form-demo/tsconfig.json",
        "packages/cli/tsconfig.json",
        "packages/dataverse-client/tsconfig.json",
        "packages/dataverse-core/tsconfig.json",
        "packages/devkit/tsconfig.json",
        "packages/eslint-plugin/tsconfig.json",
        "packages/helpers/tsconfig.json",
        "packages/testing/tsconfig.json",
        "packages/typegen/tsconfig.json",
        "packages/webapi/tsconfig.json"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 38309,
      "source_files_sampled": 197,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "CLAUDE.md",
        "packages/devkit/src/scaffold/templates/AGENT.md"
      ],
      "agent_instruction_max_bytes": 66013
    },
    "dependencies": {
      "manifests": [
        "package.json"
      ],
      "advisories": {
        "error": null,
        "scope": "published_package",
        "source": "osv",
        "findings": [],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 82,
        "malicious_count": 0,
        "assessed_package": "npm:@xrmforge/cli@0.10.8",
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm"
      ],
      "dependencies": [
        {
          "name": "@xrmforge/devkit",
          "manifest": "packages/cli/package.json",
          "ecosystem": "npm",
          "version_constraint": "workspace:*"
        },
        {
          "name": "@xrmforge/typegen",
          "manifest": "packages/cli/package.json",
          "ecosystem": "npm",
          "version_constraint": "workspace:*"
        },
        {
          "name": "commander",
          "manifest": "packages/cli/package.json",
          "ecosystem": "npm",
          "version_constraint": "^13.0.0"
        },
        {
          "name": "dotenv",
          "manifest": "packages/cli/package.json",
          "ecosystem": "npm",
          "version_constraint": "^17.4.2"
        },
        {
          "name": "@xrmforge/dataverse-core",
          "manifest": "packages/dataverse-client/package.json",
          "ecosystem": "npm",
          "version_constraint": "workspace:*"
        },
        {
          "name": "esbuild",
          "manifest": "packages/devkit/package.json",
          "ecosystem": "npm",
          "version_constraint": "^0.25.0"
        },
        {
          "name": "@azure/identity",
          "manifest": "packages/typegen/package.json",
          "ecosystem": "npm",
          "version_constraint": "^4.6.0"
        },
        {
          "name": "@xrmforge/dataverse-core",
          "manifest": "packages/typegen/package.json",
          "ecosystem": "npm",
          "version_constraint": "workspace:*"
        },
        {
          "name": "fast-xml-parser",
          "manifest": "packages/typegen/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.5.9"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "juergenbeck",
          "commits": 250,
          "avatar_url": "https://avatars.githubusercontent.com/u/19701813?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "release.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        "eslint.config.js",
        "eslint.config.mjs"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "pnpm-lock.yaml"
      ],
      "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": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 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": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 0,
            "reason": "20 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "e3fa8e976cd9e049c9ad2f3e3292e4ab55518306",
        "ran_at": "2026-07-30T13:47:40Z",
        "aggregate_score": 3.1,
        "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-27T00:21:42Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SKIPPED",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/juergenbeck/XrmForge",
    "host": "github.com",
    "name": "XrmForge",
    "owner": "juergenbeck"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "at_risk",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 46,
      "inputs": {
        "security": 45,
        "vitality": 43,
        "community": 44,
        "governance": 32,
        "engineering": 70
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "at_risk",
        "name": "Vitality",
        "value": 43,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "commits_last_year": 250,
              "human_commit_share": 1,
              "days_since_last_push": 3,
              "active_weeks_last_year": 10
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 3 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 3
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "10/52 weeks with commits",
                "points": 6.9,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 10
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "250 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 250
                    }
                  }
                ],
                "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": "critical",
            "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": 1,
            "inputs": {
              "releases_count": 0
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "no releases published",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_releases_published",
                    "params": {}
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "no releases",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_releases",
                    "params": {}
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "no releases",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_releases",
                    "params": {}
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "at_risk",
        "name": "Community & Adoption",
        "value": 44,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 1,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "1 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "good",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 77,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 65,
            "inputs": {
              "packages": [
                "@xrmforge/cli",
                "@xrmforge/devkit",
                "@xrmforge/webapi",
                "@xrmforge/helpers",
                "@xrmforge/testing",
                "@xrmforge/typegen",
                "@xrmforge/eslint-plugin",
                "@xrmforge/dataverse-core"
              ],
              "dependents": null,
              "ecosystems": "npm",
              "total_downloads": null,
              "monthly_downloads": 7492
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "7,492 downloads/month across npm",
                "points": 51.7,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 7492,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 32,
        "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": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 33,
            "inputs": {
              "followers": 0,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "juergenbeck",
              "public_repos": 3,
              "account_age_days": 3710
            },
            "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": "0 followers of juergenbeck",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 0,
                      "login": "juergenbeck"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "3 public repos, account ~10 yr old",
                "points": 16.4,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 3
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 10
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "@xrmforge/cli",
                "@xrmforge/devkit",
                "@xrmforge/webapi",
                "@xrmforge/helpers",
                "@xrmforge/testing",
                "@xrmforge/typegen",
                "@xrmforge/eslint-plugin",
                "@xrmforge/dataverse-core"
              ],
              "ecosystems": "npm",
              "any_deprecated": false,
              "min_days_since_publish": 8
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "8 package(s) on npm",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 8,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 8 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 8
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "60 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 60
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 70,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 80,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "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": "eslint.config.js, eslint.config.mjs",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "eslint.config.js, eslint.config.mjs"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "topics": [],
              "has_wiki": false,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 45,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 31,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 15,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 3,
              "scorecard_aggregate": 3.1
            },
            "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 dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 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": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "20 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "excellent",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the npm:@xrmforge/cli@0.10.8 runtime dependency closure — what installing the published package pulls in — 82 packages. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_published",
                "params": {
                  "package": "npm:@xrmforge/cli@0.10.8",
                  "assessed": 82
                }
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 0,
              "affected_packages": 0,
              "assessed_packages": 82,
              "unassessed_packages": 0,
              "affected_by_severity": "none",
              "direct_affected_packages": 0
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "no direct dependency carries a known advisory",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "no_direct_advisories",
                    "params": {}
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "no indirect dependency carries a known advisory",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "no_indirect_advisories",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory carries a publication date",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_no_publication_date",
                    "params": {}
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "excellent",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 82,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "good",
        "name": "AI Readiness",
        "value": 71,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.92,
              "agent_instruction_files": [
                "CLAUDE.md",
                "packages/devkit/src/scaffold/templates/AGENT.md"
              ],
              "agent_instruction_max_bytes": 66013
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md, packages/devkit/src/scaffold/templates/AGENT.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md, packages/devkit/src/scaffold/templates/AGENT.md"
                    }
                  }
                ],
                "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": "92 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 92,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 62,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "pnpm-lock.yaml"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "examples/account-form-demo/tsconfig.json",
                "packages/cli/tsconfig.json",
                "packages/dataverse-client/tsconfig.json",
                "packages/dataverse-core/tsconfig.json",
                "packages/devkit/tsconfig.json",
                "packages/eslint-plugin/tsconfig.json",
                "packages/helpers/tsconfig.json",
                "packages/testing/tsconfig.json",
                "packages/typegen/tsconfig.json",
                "packages/webapi/tsconfig.json"
              ],
              "agent_commit_share": 0.04,
              "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": "eslint.config.js, eslint.config.mjs",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "eslint.config.js, eslint.config.mjs"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "examples/account-form-demo/tsconfig.json, packages/cli/tsconfig.json, packages/dataverse-client/tsconfig.json, packages/dataverse-core/tsconfig.json, packages/devkit/tsconfig.json, packages/eslint-plugin/tsconfig.json, packages/helpers/tsconfig.json, packages/testing/tsconfig.json, packages/typegen/tsconfig.json, packages/webapi/tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "examples/account-form-demo/tsconfig.json, packages/cli/tsconfig.json, packages/dataverse-client/tsconfig.json, packages/dataverse-core/tsconfig.json, packages/devkit/tsconfig.json, packages/eslint-plugin/tsconfig.json, packages/helpers/tsconfig.json, packages/testing/tsconfig.json, packages/typegen/tsconfig.json, packages/webapi/tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "4 of the last 100 commits agent-authored or agent-credited",
                "points": 8,
                "status": "partial",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 4,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "primary_language": "TypeScript",
              "largest_source_bytes": 38309,
              "source_files_sampled": 197,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "TypeScript (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "TypeScript"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/197 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 197,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "examples"
              ],
              "has_mcp_signal": false,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "examples",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "examples"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "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",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-30T13:47:45.263860Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/j/juergenbeck/XrmForge.svg",
  "full_name": "juergenbeck/XrmForge",
  "license_state": "standard",
  "license_spdx": "MIT"
}

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

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

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