mcpField
A
audit-reproducibility

audit-reproducibility

github.com/pedrohcgs数据与数据库1.7k61 次安装4 周前更新
代码仓库

关于 audit-reproducibility

用于复现性审计的技能,可将论文中的数值主张与分析输出及其他声明展示位置进行核对。它按记录的容差判定各项结果,并定位不一致之处,而不预设论文或代码哪一方正确。

主要功能

  1. 01提取论文中的数值主张
  2. 02将主张匹配至分析输出
  3. 03按记录的容差核对数值
  4. 04比较各声明展示位置的数值
  5. 05逐项报告审计结论
  6. 06保存主张清单与审计报告

适用场景

  1. 01投稿前检查论文数值
  2. 02查找幻灯片或补充材料中的旧数值
  3. 03核验复现包中的结果
  4. 04重大修订后审计分析结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pedrohcgs/claude-code-my-workflow --skill audit-reproducibility

安装后在 Claude Code 中输入/audit-reproducibility确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
marker.txt
evals/cases/
manuscript-not-oracle.md
tolerance-before-comparison.md

详细介绍

审计可复现性

将稿件中的数值声明(点估计、标准误、p 值、计数)与分析流水线实际生成的输出进行比较。根据 .claude/rules/replication-protocol.md 中定义的容差阈值,逐项报告 PASS / FAIL。

核心原则:如果论文写着 ATT = -1.632 (0.584),而代码生成的结果是 -1.628 (0.591),我们会通过数值验证差异是否在文档规定的容差范围内。不再凭感觉判断“看起来差不多”。

两个方向,而非一个。 纵向检查每项声明是否与生成该声明的输出一致。横向检查它是否与展示相同数字的所有其他产物一致——补充材料表格、幻灯片、海报。纵向检查可能顺利通过,但幻灯片引用的却是上个月的数值;只有横向检查能发现这一点。已声明的展示位置列在 passport 的 appears_in 列表中——参见 replication-protocol.md → 横向检查。

适用时机

  • **提交前。**发现“我更新了分析,却忘了更新表 2”这类问题。
  • **使用同一组数字进行展示或教学前。**发现最后一次重新运行后从未重新生成的幻灯片、海报或补充材料。
  • **发布复现包前。**验证代码确实能复现论文结果。
  • **重大修订后。**确保论文仍与最新代码一致。
  • **/commit 的质量门禁。**在更改稿件和分析内容前,配合提交前调用使用。

输入

  • $0 — 稿件路径(.tex、.qmd、.md、.pdf)。必填。
  • $1 — 输出目录路径。默认为 output/,所有语言的流水线都会将结果写入此目录(R、通过 /stata-replication 使用的 Stata、Python)。已识别的其他目录:_targets/objects/(R targets 工作流),以及用户指定输出所在的任何目录。如果 output/ 不存在,但存在 v2.6 之前的 scripts/<lang>/_outputs/,则使用该目录,并在报告中说明。

工作流

阶段 0:预检
  1. 阅读 replication-protocol.md,了解当前生效的容差阈值。
  2. 确认输出目录存在且非空。如果目录为空或已过期(比稿件更早),提示用户重新运行流水线(例如 Rscript scripts/R/00_run_all.R),然后再进行审计。
  3. 确保输出目录中存在 sessionInfo.txt 或同等的环境记录文件。
阶段 1:从稿件中提取声明

解析稿件中的数值声明。匹配模式包括:

  • 点估计 + SE:ATT = -1.632 (0.584)、$\beta = 0.342$ (0.091)、带星号表示显著性的 hat{\tau} = 1.28**
  • 表格单元格:LaTeX 表格环境中的 & -1.632$^{***}$ & 0.584 &
  • 计数:our sample of 2,847 firms、$N = 2{,}847$
  • 汇总统计量:mean = 0.423、SD = 0.087
  • P 值:p < 0.01、$p = 0.003$

将每项声明记录为一个元组:

{
  claim_id: "Table2_col3_ATT",
  location: "Table 2, Column 3, row 'Treatment'",
  kind: "point_estimate" | "standard_error" | "p_value" | "count" | "percentage",
  reported_value: -1.632,
  uncertainty: 0.584,              # only for point estimates
  significance_stars: 3,            # 0-3 or None
  raw_context: "the ATT estimate of -1.632 (0.584) indicates..."
}

将提取出的声明写入 quality_reports/reproducibility_claims_[manuscript-name].json,以便用户在审计前检查提取结果。

阶段 2:从输出中提取结果

在 $1 中扫描对应的数值。优先顺序:

  1. .rds 文件 — 使用 readRDS(path)$coef[["treatment"]] 形式的查找。可使用 Rscript -e "saveRDS(summary(readRDS(...)), '/tmp/audit.rds')" 进行提取。
  2. .tex 表格 — 直接解析 LaTeX 表格单元格;根据列标题 + 行标签进行匹配。
  3. .csv 汇总文件 — 使用 pandas/readr 解析,并按键值查找。
  4. .out / .log 文件(Stata、回归输出)— 使用正则表达式提取。
  5. .json — 直接按键查找。

记录每项提取出的结果:

{
  source: "output/results.rds",
  lookup_key: "fit_main$coefficients['treated']",
  value: -1.628,
  uncertainty: 0.591,
  p_value: 0.005
}
阶段 2b:收集每个声明的展示位置(护照模式)

对于每项护照声明,读取其 appears_in 列表,并提取各条目中实际显示的值:

{
  claim_id: "C3",
  displays: [
    { path: "manuscript.tex", locator: "Table 1, Col 2", display_precision: 3, shown: 0.342 },
    { path: "Slides/Lecture04_Results.tex", locator: "frame 'Main result'", display_precision: 2, shown: 0.34 }
  ]
}

本轮规则:

  • 主要的 location: 也是展示位置之一,并非独立的一项——如果 appears_in 中重复出现它,那就算一个展示位置,而不是两个。
  • 无法解析的已声明展示位置——文件缺失,或 locator 处没有值——记录为 shown: NOT_FOUND。 在阶段 4c 中,它将判定为 FAIL。绝不可跳过:悄然停止匹配的 locator,正是隐藏过时数字的地方。
  • 没有 appears_in 列表的声明,表示横向未核查,而非横向无误。应如实报告(见阶段 5),不要默默判定为通过。
阶段 3:将声明与结果匹配

精确标签不匹配时,使用模糊启发式方法:

  • 名称相似度("treatment effect" ~ "ATT" ~ "treated")
  • 数值相似度(如果两个候选值与报告值相差不超过 10%,优先选择 SE 更接近的候选值)
  • 声明 raw_context 字段中的上下文提示(表格编号、行标签、描述)

为每项声明生成一个匹配候选项,并给出置信度评分。置信度低于 0.7 的声明应标记为“UNMATCHED — 需要人工审核”,而不是默默判定为通过。

阶段 4:容差检查

对于每项匹配的声明,应用 replication-protocol.md 中的阈值:

类型容差示例
整数(N、计数)精确相等2,847 必须等于 2,847
点估计abs(reported - computed) < 0.01-1.632 与 -1.628 → 差值 = 0.004 → PASS
标准误abs(reported - computed) < 0.050.584 与 0.591 → 差值 = 0.007 → PASS
P 值显著性水平相同p<0.01 且 p<0.01 → PASS;p<0.01 且 p=0.03 → FAIL
百分比±0.1 个百分点42.3% 与 42.35% → PASS

遵循用户在其 replication-protocol.md 分支中写明的任何容差覆盖设置(他们可能因 MC 噪声而放宽容差,也可能针对行政数据而收紧容差)。

阶段 4b:处置结果——PASS / FAIL / EXPLAINED / UNMATCHED

容差检查会得出以下四种处置结果之一:

  • PASS — 在容差范围内。
  • FAIL — 超出容差,且没有记录站得住脚的替代方案。会阻止通过(退出代码为 1)。
  • EXPLAINED — 超出容差,但作者已记录一个具体且具名的替代规格来解释差异(见降级规则)。会在报告中列出并纳入给审稿人的回复;不会阻止通过。
  • UNMATCHED — 找不到计算结果作为对应项(阶段 3 的置信度 < 0.7)。绝不可自动降级。

不匹配并不自动意味着失败。 在应用研究中,最常见的超出容差结果是站得住脚的替代规格,而非错误——例如 reghdfe 与 feols 的聚类 df、不同的带宽选择规则、不同的 MC 种子或重复次数,或展示时的舍入。该技能的职责是为人工审核者整理分歧,而不是断言代码正确、论文错误。(下文“Stata 专项说明”中的 df 调整说明,是具名替代方案的典型示例。)

稿件并非裁判标准。 当计算值与稿件不符时,不要想当然地认为代码正确而论文过时,也不要反过来。重构可能破坏了此前正确的表格(磁盘上的输出才是有问题的),也可能是论文沿用了旧数字。计算值是一个挑战者,而非事实标准。报告不匹配时,应写成“{论文,代码} 中必有一方需要修改——查明是哪一方”,绝不要写成“回退代码以匹配论文”。这样可以避免为了让论文“复现”而回退真正的错误修复。

降级规则:FAIL → EXPLAINED

只有当针对该确切主张,在通行证条目的 notes: 字段(通行证模式)或审计报告的作者备注列(默认模式)中记录了明确点名的替代方案时,才可以将 FAIL 降级为 EXPLAINED。有效备注示例:

"reghdfe 与 feols 的聚类自由度调整不同;采用 reghdfe 的小样本校正后,已发布的值为 −1.19,与脚本的 −1.187 在舍入误差范围内。待修正代码。"

作者是审计员:该技能会安排双向比较(同时显示报告值和计算值);人工撰写一行点名替代方案的备注;该技能记录此备注,并在之后遵循它。将处理结果标记为 PAPER-CORRECTED、CODE-CORRECTED 或 DEFENSIBLE-ALTERNATIVE。

不可逾越的底线——绝不能降级为 EXPLAINED:

  • 空白备注、“不清楚”、“看起来没问题”,或任何未点名具体替代规格的备注。
  • UNMATCHED 主张(没有可供比较的计算结果)。
  • 没有提供替代方案的直接数值矛盾。

(引文/存在性主张不在此范围内——由 /verify-claims 负责,并在其范围内采用同样的点名替代方案宽免规则。)

重复出现 EXPLAINED 是一种信号(两次规则)

沿用 review-paper --adversarial 和 summary-parity.md 中的两次规则:如果同一条主张在连续两次审计中被降级为 EXPLAINED,却从未被修正为 PASS(作者不断援引替代方案,却从未更新论文或代码),就不要再把它当作已悄然解决的问题。在 Phase 5 中醒目指出——"这一存在争议的数字已两次被标记为 EXPLAINED,却从未得到修正"——这样,未解决的分歧就不能无限期地藏在已记录的备注后面。在通行证模式下,通过将当前的 status/notes 与上次审计进行比较来检测。

Phase 4c:横向检查——各处展示的内容必须彼此一致

Phase 4 将主张与代码进行比较。本阶段对 Phase 2b 收集的 displays 进行两两比较,检查该主张在各处的展示是否彼此一致:

  1. 按较低的精度对双方进行舍入。 两处展示值在都舍入到两者 display_precision 中较小值所对应的精度后相等,即视为一致。幻灯片中的 0.34 与论文中的 0.342 在 2 位小数精度下相符;0.29 与 0.342 则不符。
  2. 不适用容差。 tolerance: 块用于纵向比较,即比较两个测量值。同一个数值的两处展示是两个副本——按步骤 1 舍入后,它们要么匹配,要么不匹配。
  3. 按 Phase 4b 的术语给出处置结果:
    • PASS — 每一对展示都一致。
    • FAIL — 任一对展示不一致,或任一处展示为 NOT_FOUND。绝不能降级为 EXPLAINED:点名替代规格可以解释代码与论文为何不同,却无法解释同一个数值的两个副本为何不同。如果某处展示的确采用了不同规格,那就是另一条主张,应单独建立通行证条目。
    • UNCHECKED — 该主张未声明 appears_in 列表。报告此情况,但不阻止流程;修复方法是声明这些展示位置。
  4. 报告哪一处发生了变化,而不是哪一处正确。 指出其值与其他位置不同的产物,以及它最后一次修改的时间;由作者决定是幻灯片过时还是论文过时。不要手动编辑某处展示值来匹配对应展示——应根据该主张所指向的输出重新推导两者(replication-protocol.md → 反模式)。
Phase 5:报告

撰写 quality_reports/reproducibility_audit_[manuscript-name].md:

# Reproducibility Audit: [Manuscript Title]

**Date:** [YYYY-MM-DD]
**Manuscript:** [path]
**Outputs directory:** [path]
**Tolerance source:** .claude/rules/replication-protocol.md

## Summary

| Status | Count |
|---|---|
| PASS (vertical and horizontal) | N |
| FAIL (diff > tolerance, no named alternative) | M |
| EXPLAINED (out of tolerance, named alternative recorded) | E |
| UNMATCHED (manual review) | K |
| HORIZONTAL DRIFT (declared displays disagree, or a display not found) | H |
| HORIZONTALLY UNCHECKED (claim declares no `appears_in`) | U |
| **Overall verdict** | **PASS / FAIL** (FAIL iff M > 0 or H > 0; EXPLAINED does not fail the audit) |

## PASS (all within tolerance)
| Claim | Reported | Computed | Diff | Tolerance |
|---|---|---|---|---|
| Table2_col3_ATT | -1.632 (0.584) | -1.628 (0.591) | 0.004 / 0.007 | 0.01 / 0.05 |

## FAIL (outside tolerance — BLOCKER)
| Claim | Reported | Computed | Diff | Tolerance | Location in paper | Author note (name a concrete alternative to downgrade → EXPLAINED) |
|---|---|---|---|---|---|---|

## EXPLAINED (out of tolerance; defensible named alternative recorded — non-blocking, carry into response-to-referees)
| Claim | Reported | Computed | Named alternative (why the gap is defensible) | Resolution |
|---|---|---|---|---|
| Table3_col2_ATT | -1.187 | -1.19 | reghdfe vs feols clustering-df adjustment | DEFENSIBLE-ALTERNATIVE |

## UNMATCHED (manual review)
| Claim | Raw context | Candidate sources |
|---|---|---|

## HORIZONTAL DRIFT (one number, two values — BLOCKER)
| Claim | Display A | Display B | Compared at | Values | Which side moved |
|---|---|---|---|---|---|
| C3 | manuscript.tex — Table 1, Col 2 | Slides/Lecture04_Results.tex — frame 'Main result' | 2 decimals | 0.34 vs 0.29 | deck older than the claim's output_file |

## HORIZONTALLY UNCHECKED (no `appears_in` declared)
| Claim | Primary display | Other artifacts to declare |
|---|---|---|

## Environment
[sessionInfo excerpt]

## Next steps
1. Resolve each FAIL row — either correct the manuscript, rerun the analysis, or (if the gap is a defensible alternative spec) record a concrete named alternative to downgrade it to EXPLAINED.
2. Resolve each HORIZONTAL DRIFT row — regenerate the lagging display from the claim's output, never by retyping the other display's value. A `NOT_FOUND` display is fixed by correcting the `locator`, not by dropping the entry.
3. Review UNMATCHED rows — add explicit lookup keys or widen the search scope.
4. Declare `appears_in` for the HORIZONTALLY UNCHECKED rows — every artifact a reader sees the number in.
5. Review EXPLAINED rows before submission — each should map to a sentence in the response-to-referees.
6. After zero FAILs and zero HORIZONTAL DRIFT (EXPLAINED rows allowed), the paper is replication-ready.
阶段 5b:追加到复现日志

每次运行都会重写上面的报告和护照,因此两者都不会保留历史记录。日志会保留。每次运行后——无论是默认模式还是护照模式——都要向 quality_reports/replication-log.md 追加一个区块。日志会提交到仓库,因此合作者或数据编辑者无需阅读代码,就能看到检查了什么、依据哪个提交,以及每个数字是如何得出的。

  • 仅追加。 使用 >> 写入;绝不编辑或重新排序过去的区块。需要更正时,应重新运行,而不是编辑。仓库卫生检查会拒绝编辑或删除已提交行的提交——提交前钩子会对照上一个提交检查,CI 会对照此次工作合并到的分支检查。它能证明没有条目被编辑,但不能证明每次运行都已记录。披露内容脱敏是唯一例外:使用 ALLOW_LOG_REWRITE=1 提交,并在提交消息中说明原因。
  • 标注修订版本。 记录短提交哈希;如果 quality_reports/ 以外的任何内容尚未提交(包括未跟踪文件),则在哈希后加上 -dirty——这样未提交代码的评估结果会明确标示出来,而审计自身的报告文件不会触发此标记。在 Git 仓库之外,或首次提交之前,标记为 no-commit。
  • “计算方式”必须是阶段 2 使用的确切访问器或命令——信息应足够让他人重新计算该数字。
  • 受限数据: 日志包含与报告相同的数字,因此只有在通过披露审查后才能提交,其他输出也遵循相同规则(confidential-data.md)。
LOG=quality_reports/replication-log.md
[ -f "$LOG" ] || printf '%s\n' "# Replication log" "" \
  "Append-only record of /audit-reproducibility runs: what was checked, against which commit, and how each number was computed. Never edit a past entry; a correction is a new run." "" > "$LOG"
# -dirty = anything uncommitted outside quality_reports/, untracked files included (the audit writes its own files in quality_reports/)
if REV=$(git rev-parse --short HEAD 2>/dev/null); then
  [ -n "$(git status --porcelain -- . ':(exclude)quality_reports' 2>/dev/null)" ] && REV="$REV-dirty"
else
  REV="no-commit"   # not a git repository, or nothing committed yet
fi
printf '## %s — %s @ %s\n\n' "$(date +%F)" "<manuscript path>" "$REV" >> "$LOG"
# Quoted heredoc: nothing below is expanded, so an accessor's `$` or backtick is written as-is.
cat >> "$LOG" <<'EOF'
Outputs: `<outputs dir>` · Verdict: **<PASS|FAIL>** (<M> FAIL, <H> horizontal drift, <E> EXPLAINED, <K> unmatched)

| Claim | Location | Reported | Computed | How computed | Tolerance | Verdict |
|---|---|---|---|---|---|---|
| Table2_col3_ATT | main.tex: Table 2, col 3 | -1.632 | -1.628 | `readRDS("output/results.rds")$coef[["treatment"]]` | 0.01 | PASS |

EOF

每个经审计的主张占一行,顺序与报告一致。

退出行为

  • 全部为 PASS(或 PASS + EXPLAINED): 退出码为 0,并打印摘要。
  • 任何 HORIZONTAL DRIFT: 退出码为 1,与 FAIL 同等处理——两个产物对同一数字显示不同值,无论哪一个正确,都是缺陷。HORIZONTALLY UNCHECKED 行会发出警告(退出码为 0)并列出。
  • 任何 FAIL: 退出码为 1,并将摘要打印到 stderr。这使该技能可用作 /commit 的提交前检查——执行模式见 replication-protocol.md。EXPLAINED 行不计为 FAIL,也不会触发退出码 1——它们会显示出来,但不会阻止提交。对于真正的 FAIL(没有注明替代方案)以及伪造或 UNMATCHED 的主张,检查仍会严格把关。
  • UNMATCHED > 0(且 FAIL 为 0): 退出码为 0,但会发出警告——用户必须手动审查。

源语言覆盖范围

该技能会将稿件中的主张与三种源语言生态系统的输出进行比较。三者都写入同一个 output/ 目录:

源语言默认输出目录通过以下方式读取输出常见主张来源
R(默认)output/readRDS()、arrow::read_parquet()、vroom::vroom().rds / .parquet / .csv / tinytable .tex
Stata(v1.9.0)output/从 R 调用 haven::read_dta(),或从 Python 调用 pyreadstat.read_dta().dta / esttab .tex / .smcl 日志值
Pythonoutput/(或 _targets/)pandas.read_parquet、pickle.load.parquet / .pickle / .csv

Stata 专属说明(v1.9.0):

  • .dta 输出可通过 haven::read_dta()(R)、pyreadstat.read_dta()(Python)读取;如果稿件引用的是表格单元格中的值,也可以解析对应的 esttab .tex。
  • 稿件单元格 \input{output/tab_main.tex} 是最有力的溯源信号——该单元格的值由 .do 文件机械生成。将 .tex 中的位置与 03_analyze.do 中的回归调用匹配。
  • reghdfe 和基础命令 reg, cluster() 的聚类 df 调整可能不同。如果标准误在小数点后第 2 位不匹配,replication-protocol.md 中的容差规则适用;如果在小数点后第 1 位不匹配,则应调查 df 调整。

护照模式(v1.9.0)

当 quality_reports/passports/<paper-slug>.yaml 存在时,该技能会以 护照模式 运行:不再一次性生成报告,而是读取、更新并原地重写护照文件。

  • 对护照中的每个 claims: 条目,执行与默认模式相同的数值审计(从稿件的 location 提取报告值,在 source_file:source_line / output_file:output_field 处定位计算值,并与 tolerance: 比较),然后对该条目的 appears_in 列表执行 Phases 2b 和 4c 的横向检查。
  • 审计每项声明后,原地更新其 status:
    • PASS → 声明在容差范围内,并且每个已声明的展示值都与更粗精度下的结果一致。
    • FAIL → 声明超出容差,且条目的 notes 未注明具体的替代规范;或两个已声明的展示值不一致;或无法在文件中定位某个 appears_in 条目。在 notes 中记录差异——纵向 FAIL 记录报告值与计算值,横向 FAIL 记录两个展示值及其路径。阻止通过(退出码为 1)。
    • EXPLAINED → 声明超出容差,但条目的 notes 已记录具体命名的替代规范(非空,也不是“unclear”)。技能在下次运行时会读取 notes,并将同一项超出容差的声明判定为 EXPLAINED,而不是 FAIL——会显示,但不阻止通过。仍适用硬性底线:UNMATCHED 声明或未注明替代规范的备注仍判定为 FAIL。
    • STALE → 如果 source_file、output_file 或任何 appears_in 路径的修改时间晚于 last_verified_on,则标记为 STALE 并重新运行审计逻辑(重新审计后,状态将变为 PASS / FAIL / EXPLAINED——STALE 是临时状态)。
  • 按声明更新 last_verified_on 和 last_verified_by: "/audit-reproducibility"。
  • 更新顶层的 paper.last_audit。

如果检测到稿件中有声明没有匹配的护照条目,则发出 UNVERIFIED 警告——作者应添加该条目(护照范围由作者维护,而非自动填充,以避免错误推断)。

护照模式不会删除护照条目。如果某项声明从稿件中消失,其护照条目仍会保留,并标记为 STALE——由作者决定是删除该条目(声明已撤回),还是更新其 location(声明已移动)。

它也不会自行添加 appears_in 条目。如果横向检查恰好发现某个展示——护照未声明、但在演示文稿或补充材料中出现相同值——则会在 HORIZONTALLY UNCHECKED 表中将其报告为建议,由作者决定是否声明。理由与上述不自动填充规则相同:推断出的展示实际上可能是另一种量值,导致横向检查一直失败。

有关完整架构和集成点(/commit、/review-paper),请参阅 .claude/rules/replication-protocol.md 中的“声明溯源:passport.yaml”。

交叉引用

此技能不做什么

  • 不重新运行分析。 此技能将当前输出与手稿中的论断进行比较。如果输出已过时,请先重新运行流水线(预检阶段会发出警告)。
  • 不检查错误的设定。 一个能顺利编译并生成可复现 -1.632 的回归结果,就是可复现的。-1.632 是否代表正确的估计对象,则属于 review-paper / 领域审稿人的审查范畴。
  • 不检查外部软件包版本。 sessionInfo.txt 捕获结果可供审稿人查看环境;版本固定由用户负责(通过 renv.lock 或 DESCRIPTION 文件)。

长时间批量重跑:使用 Monitor 工具(2026 年 4 月)

当要求 /audit-reproducibility 验证论文中的所有数值论断时,最稳妥的做法是重新运行完整流水线(00_run_all.R 或同等程序),并将重新生成的输出与手稿中的数值进行比较。对于运行时间超过几分钟的流水线,可在后台启动重跑,并使用 Anthropic 的 Monitor 工具(2026 年 4 月第 15 周)流式查看 stdout。这样,审计过程可在运行期间及时响应错误,而不必等到整个流水线完成后才发现某一步骤失败。

评论

支持 Markdown