关于 entropy-check
Entropy 是只读技能,用于评估仓库各领域的文档及相关质量信号。它按适用信号评级并报告结果,可更新 QUALITY_SCORE.md;它负责检测问题,不会修复问题。
主要功能
- 01依据机械信号评定领域质量
- 02检查文档覆盖度与时效性
- 03评估流程、问题、测试和 Hookshot 信号
- 04支持 PR 范围扫描和全仓扫描
- 05将扫描结果写入 QUALITY_SCORE.md
适用场景
- 01PR 合并后检查文档情况
- 02定期扫描整个仓库的质量
- 03重构前获取当前健康概况
- 04找出文档过时或缺失的领域
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fellowship-dev/ dogfooded-skills --skill entropy-check安装后在 Claude Code 中输入/entropy-check确认已加载。
附带文件scripts/contents-writeback.shtests/contents-writeback.test.sh
详细介绍
熵
传感器。检测、评级、报告。绝不修复。
通过 npx 安装:
npx skills add fellowship-dev/dogfooded-skills/skills/ops/entropy-check
适用场景
- 事件驱动(PR 合并): PR 合并后触发。扫描受影响的领域,检查文档是否过时。
- 每周 cron(全面扫描): 针对每个产品仓库运行,使用最新评级更新 QUALITY_SCORE.md。
- 手动: 怀疑文档已与实际情况不符,或想在重构前获取当前健康状况快照时。
功能
Entropy 是只读传感器。它根据机械信号计算领域质量评级:
| 信号 | 衡量内容 | 权重 |
|---|---|---|
| 文档覆盖率 | docs/code-structure.md 是否涵盖此领域? | 二元 |
| 流程覆盖率 | 是否为关键路径定义了 FlowChad 流程? | 二元(仅限前端仓库) |
| 过时程度 | 距上次代码提交与上次文档更新的天数 | >30 天 = 过时 |
| 未解决问题 | GitHub 中标记为此领域的问题 | >3 个未解决 = 信号 |
| 测试覆盖率 | 来自可用的覆盖率报告 | <60% = 信号 |
| Hookshot 覆盖率 | doc-coverage.json 是否与文档保持同步? | 过时程度 |
评级标准:
- A — 所有适用信号均为绿色
- B — 1 个适用信号缺失或为黄色
- C — 2 个适用信号缺失
- D — 3 个或更多适用信号缺失
- F — 此领域完全没有文档
不适用的信号不计入评级分母。某仓库有 4 个适用信号且全部通过 = A 级。
说明
0. 确定目标仓库
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename $(git remote get-url origin) .git)
ORG=$(git remote get-url origin | sed 's/.*github.com[:/]\([^/]*\).*/\1/')
FULL_REPO="$ORG/$REPO_NAME"
TODAY=$(date +%Y-%m-%d)
如果 QUALITY_SCORE.md 存在,则读取它——这是要更新的基线。
如果 ARCHITECTURE.md 存在,则读取它——提取领域列表。
如果两者都不存在,则根据目录结构推断领域(顶层源代码目录,每个目录对应一个领域)。
1. 确定信号是否适用
评级前,确定哪些信号适用于此仓库。记录报告中的适用情况。
前端检测(S2 FlowChad)
HAS_FRONTEND=false
if [ -f "$REPO_ROOT/package.json" ]; then
grep -q '"next"\|"react"\|"vue"\|"angular"\|"svelte"' "$REPO_ROOT/package.json" && HAS_FRONTEND=true
fi
if [ -d "$REPO_ROOT/app" ] || [ -d "$REPO_ROOT/pages" ] || [ -d "$REPO_ROOT/src/components" ]; then
HAS_FRONTEND=true
fi
如果 HAS_FRONTEND=false:S2 FlowChad 不适用——不计入评级分母。
Hookshot 检测(S6)
HOOKSHOT_EXISTS=false
git -C "$REPO_ROOT" log -1 --format="%ci" -- .claude/doc-coverage.json 2>/dev/null | grep -q . && HOOKSHOT_EXISTS=true
[ -f "$REPO_ROOT/.claude/doc-coverage.json" ] && HOOKSHOT_EXISTS=true
S6 始终适用,但评分说明有所不同——见下文的信号 6。
2. 确定范围
PR 触发模式: 给定 PR 编号,识别发生变更的文件:
# Get changed files from PR
GH_TOKEN=$GH_TOKEN gh api repos/$FULL_REPO/pulls/{PR_NUMBER}/files \
--jq '.[].filename'
根据目录前缀将每个变更文件映射到相应领域。仅对有变更的领域评级。
全面扫描模式: 对所有领域评级。从 ARCHITECTURE.md 读取领域列表,或自行推断。
3. 按领域评级
对范围内的每个领域:
信号 1:文档覆盖率
# Is this domain mentioned in docs/code-structure.md?
grep -i "{domain_name}" $REPO_ROOT/docs/code-structure.md 2>/dev/null && echo "COVERED" || echo "MISSING"
评分:✅ 已覆盖 / ❌ 缺失
信号 2:FlowChad 覆盖率
如果 HAS_FRONTEND=false,则完全跳过。 在适用性表中记录为“ N/A — 未检测到前端”。
# Only run if HAS_FRONTEND=true
if [ "$HAS_FRONTEND" = "true" ]; then
ls $REPO_ROOT/.flowchad/flows/ 2>/dev/null | grep -i "{domain_slug}" && echo "COVERED" || echo "MISSING"
fi
评分(仅限前端):✅ 有流程 / ❌ 无流程
信号 3:过时程度
查找此领域中文件最近一次代码提交的日期:
# Last commit touching domain files
git -C $REPO_ROOT log -1 --format="%ci" -- "{domain_directory_glob}" 2>/dev/null
查找此领域文档部分的最后更新日期:
# Last commit touching docs/code-structure.md
git -C $REPO_ROOT log -1 --format="%ci" -- docs/code-structure.md 2>/dev/null
差值 = 最近一次代码提交与最近一次文档更新之间的天数。 评分:✅ 差值 ≤30 天 / ⚠️ 差值 31-60 天(黄色) / ❌ 差值 >60 天
信号 4:未解决问题
# Open issues tagged to this domain (use domain name as label or search term)
GH_TOKEN=$GH_TOKEN gh issue list --repo $FULL_REPO \
--state open --search "{domain_name}" \
--json number,title | jq length 2>/dev/null || echo "0"
评分:✅ 0-3 个未解决 / ⚠️ 4-6 个未解决(黄色) / ❌ >6 个未解决
信号 5:测试覆盖率
# Look for coverage reports
ls $REPO_ROOT/coverage/ $REPO_ROOT/.nyc_output/ $REPO_ROOT/tmp/coverage/ 2>/dev/null
cat $REPO_ROOT/coverage/index.html 2>/dev/null | grep -o '[0-9.]*%' | head -1
# Rails: simplecov
cat $REPO_ROOT/coverage/.last_run.json 2>/dev/null | jq '.result.covered_percent'
如果没有可用的覆盖率报告 → 跳过此信号(视为中性,而非 ❌)。 评分:✅ ≥80% / ⚠️ 60-79% / ❌ <60%(仅在有覆盖率数据时)
信号 6:Hookshot 覆盖率过时情况
COVERAGE_DATE=$(git -C $REPO_ROOT log -1 --format="%ci" -- .claude/doc-coverage.json 2>/dev/null)
DOCS_DATE=$(git -C $REPO_ROOT log -1 --format="%ci" -- docs/code-structure.md 2>/dev/null)
区分以下两种情况:
-
未配置 Hookshot —
.claude/doc-coverage.json从未存在过(没有该文件的 git 历史记录,且文件不存在): 评分:❌“未配置 Hookshot — 建议进行设置” -
Hookshot 已过时 — 文件曾存在过(找到 git 历史记录),但文档更新得更近: 评分:⚠️“Hookshot 已过时(覆盖率 {COVERAGE_DAY} < 文档 {DOCS_DAY})”——使用提交日期,而非天数
-
Hookshot 是最新的 — 覆盖率的更新时间晚于或与文档更新日期相同: 评分:✅“Hookshot 是最新的”
检测逻辑:
if [ -z "$COVERAGE_DATE" ] && [ ! -f "$REPO_ROOT/.claude/doc-coverage.json" ]; then
# Never been set up
S6_SCORE="❌"
S6_NOTE="Hookshot not configured — recommend setup"
elif [ -n "$DOCS_DATE" ] && [ -n "$COVERAGE_DATE" ]; then
# Both exist — compare dates
DOCS_EPOCH=$(date -d "$DOCS_DATE" +%s 2>/dev/null || date -j -f "%Y-%m-%d %H:%M:%S %z" "$DOCS_DATE" +%s 2>/dev/null)
COV_EPOCH=$(date -d "$COVERAGE_DATE" +%s 2>/dev/null || date -j -f "%Y-%m-%d %H:%M:%S %z" "$COVERAGE_DATE" +%s 2>/dev/null)
STALE_DAYS=$(( (DOCS_EPOCH - COV_EPOCH) / 86400 ))
if [ "$STALE_DAYS" -gt 0 ]; then
S6_SCORE="⚠️"
S6_NOTE="Hookshot stale (coverage ${COVERAGE_DATE%% *} < docs ${DOCS_DATE%% *})"
else
S6_SCORE="✅"
S6_NOTE="Hookshot current"
fi
else
S6_SCORE="❌"
S6_NOTE="Hookshot not configured — recommend setup"
fi
计算等级
仅收集适用的信号。只统计适用信号中的失败信号(❌):
APPLICABLE_SIGNALS = all signals minus inapplicable ones
FAILING = count of ❌ in APPLICABLE_SIGNALS
YELLOW = count of ⚠️ in APPLICABLE_SIGNALS
SCORE = FAILING + (YELLOW * 0.5), rounded up
- SCORE = 0 → A
- SCORE = 1 → B
- SCORE = 2 → C
- SCORE ≥ 3 → D
- 完全没有文档 → F
示例:没有前端的仓库(跳过 S2),5 个适用信号全部为绿色 → 等级为 A,而不是 B。
4. 更新 QUALITY_SCORE.md
读取现有文件。更新每个已评分领域的行。发现新领域时添加。 保留不在范围内领域的现有行(仅更新本次重新扫描的内容)。
## Domains
| Domain | Grade | Last audit | Notes |
|--------|-------|------------|-------|
| {domain} | {grade} | {TODAY} | {brief note on what's missing, or "All signals green"} |
写入更新后的文件,然后在 History 部分追加:
| {TODAY} | {trigger: PR #{N} / weekly sweep / manual} | {N} domains scanned, {N} regressions, {N} improvements |
4a. 直接将写回内容提交到集成分支——不建分支,不开 PR
负责人决定(2026-10-07):写回内容直接提交到已扫描仓库的集成分支,并使用 [skip ci];不会创建分支或 PR。各个并行的写回 PR 都编辑了 QUALITY_SCORE.md,每次合并都会产生冲突,还会重复触发完整的审查流程。
- 分支: 仓库的默认分支,在运行时解析(
fellowship-dev/pylot使用develop,其发布流程会将develop→main)。切勿硬编码。 - 机制: 通过
scripts/contents-writeback.sh调用 GitHub Contents API,而非使用git push。Pylot worker 的git-push-guard.sh会拒绝推送到main/master/develop(按路径限定的许可位于 fellowship-dev/pylot#3555,但尚未发布);不要重试被拒绝的推送,也不要寻找其他变通方法。 - 范围: 仅限
QUALITY_SCORE.md。该辅助脚本会拒绝任何其他路径以及不含[skip ci]的消息(退出码 2)——明确报告失败并终止运行;绝不能以这种方式写入其他文件。
- 没有更改 → 仅报告。 如果等级没有变化、没有新增领域,也没有发现新的 Tooling 漂移,那么唯一的修改只会是 History 行。舍弃该修改,不提交任何内容,发布步骤 8/9 的报告并结束。
- 否则提交。 获取该分支上的当前文件,将本次运行的修改应用到该文件中,然后放入:
WB="${PYLOT_WORKSPACE:-$HOME/.claude}/skills/entropy-check/scripts/contents-writeback.sh"
BASE=$(GH_TOKEN=$GH_TOKEN gh repo view "$FULL_REPO" --json defaultBranchRef --jq .defaultBranchRef.name)
MSG="chore: entropy scan — PR #{N} {title} [skip ci]" # weekly/manual: chore: entropy scan — weekly sweep $TODAY [skip ci]
FRESH=$(mktemp)
SHA=$(GH_TOKEN=$GH_TOKEN bash "$WB" fetch "$FULL_REPO" "$BASE" "$FRESH")
将本次运行的行更新、新领域、Tooling 部分和 History 行重新应用到 $FRESH(分支上的当前内容),并确保其他所有行都与该处完全一致。然后:
GH_TOKEN=$GH_TOKEN bash "$WB" put "$FULL_REPO" "$BASE" QUALITY_SCORE.md "$FRESH" "$SHA" "$MSG"; echo "rc=$?"
rc=0 会输出提交 sha。rc=3 表示 sha 已过期(HTTP 409/422——其他写回先一步完成):重新获取文件并重新应用修改,总尝试次数最多为 3 次。任何其他代码,或第三次出现 rc=3,都表示运行失败,必须明确报告。
- 始终编辑刚刚获取的内容;绝不能用过期的本地副本覆盖它。
- 提交消息保留字面标记
[skip ci],确保该提交不会触发 CI 或部署。 - 直接提交不会创建 PR,因此不会再次触发合并后的熵规则。
- 提交成功后本次运行即完成;记录提交 URL,用于步骤 8。
5. 信号适用性部分
在每份报告输出中都包含一个「信号适用性」表格:
## Signal Applicability
| Signal | Applicable? | Reason |
|--------|------------|--------|
| S1 Doc Coverage | Yes | — |
| S2 FlowChad | No | No frontend framework detected (bash/python scripts only) |
| S3 Staleness | Yes | — |
| S4 Open Issues | Yes | — |
| S5 Tests | Yes | No test framework found — recommend adding bats for shell scripts |
| S6 Hookshot | Yes | — |
在 Reason 列中填写具体原因:
- S2 不适用:「未检测到前端框架」+ 检测到的内容(例如「仅有 bash/python 脚本」、「Go/Rails 仓库」)
- S2 适用:列出检测到的框架(例如「在 package.json 中检测到 Next.js」)
- S5 无数据:「未找到覆盖率报告——已跳过此信号」
- S5 找到数据:Reason 留空或注明覆盖率百分比
- S6:始终适用;Reason 显示 hookshot 状态详情
6. 过时情况报告
对于等级为 C、D 或 F 的领域,或等级相较之前有所下降的领域,输出一条发现:
## Entropy Findings — {TODAY}
### Regressions (grade dropped)
- {Domain}: {old grade} → {new grade}
Missing: {list of failing signals}
Last doc update: {date}
Last code commit: {date}
### Stable issues (same low grade)
- {Domain}: {grade} (unchanged since {date})
Missing: {list}
### Improvements (grade improved)
- {Domain}: {old grade} → {new grade}
### Clean (A or B)
- {Domain}: {grade}
7. 导入的 Lib 漂移(架构检查)
此检查已从维护部分移至此处。 这是架构信号,而非基础设施检查。
已废弃:导入命令漂移检查针对的是已不再使用的工作流。
跳过此步骤;无需报告 ## Tooling 漂移。
8. PR 触发的输出
由 PR 合并事件触发时,在步骤 4a 完成后(写回已提交,或仅报告),输出可直接用于评论的摘要,并将其发布到已合并的 PR 上:
## Entropy Scan — PR #{N} merged
Domains affected: {list}
Write-back: {commit URL on the default branch, or "none — no QUALITY_SCORE.md change"}
| Domain | Grade | Change | Notes |
|--------|-------|--------|-------|
| {domain} | {grade} | {→ or unchanged or ↑ or ↓} | {note} |
{If any regressions:}
⚠️ Doc staleness detected in: {domain list}
Recommended: update docs/code-structure.md for these domains before the next PR in this area.
在评论末尾包含「信号适用性」表格(见步骤 5)。
9. 全面扫描输出(每周 cron)
## Entropy Weekly Sweep — {DATE}
{N} domains across {REPO_NAME}
### Grades
{Full grade table}
### Signal Applicability
{Signal applicability table}
### Action Items
{Domains graded D or F → recorded via file-finding.sh (existing issue or weekly digest)}
对于 D/F 域,通过共享的归档助手(pylot-cli →
scripts/file-finding.sh)记录发现结果。过时的文档不会阻塞流程,因此该助手会在同一域或应用的未关闭 issue 下发表评论,或将内容追加到每周的 digest issue 中——它绝不会新建 issue:
FF="${PYLOT_WORKSPACE:-$HOME/.claude}/skills/pylot-cli/scripts/file-finding.sh"
GH_TOKEN=$GH_TOKEN bash "$FF" --repo "$FULL_REPO" \
--title "Entropy: {domain} docs critically stale (grade {grade})" \
--search "\"{domain} docs critically stale\"" \
--body "Domain **{domain}** scored **{grade}** in the weekly entropy scan.
**Missing signals:**
{list}
**Last code commit to this domain:** {date}
**Last doc update:** {date}
Fix: update \`docs/code-structure.md\` and run \`/hookshot\` to regenerate hooks."