Observe · Change · Recompute · Explain
工具不是黑盒,它是一条可复盘的流水线
Read-only · exact teaching model
代码页展示与实验一致的教学模型:输入选择、规则 gate、依赖闭包、报告与 rollback token。
这一课怎么学
- 观察
- 预测
- 改一个量
- 看证据
- 再读代码
先锁定变更集合
changed-only 与 dependency closure 决定审计边界。
// SECTION:input
export function selectInput(allAssets, changedOnly, includeDependencies) {
const roots = changedOnly ? allAssets.filter((asset) => asset.changed) : allAssets;
return includeDependencies ? dependencyClosure(roots, allAssets) : roots;
}
规则输出必须可机器消费
每条规则都提供状态与是否可自动修复。
// SECTION:rules
export function evaluateRules(asset, rules) {
return rules.map((rule) => ({
id: rule.id,
status: rule.check(asset) ? 'pass' : 'fail',
fixable: Boolean(rule.fix),
}));
}
资产不是孤立文件
缺失依赖要变成报告中的显式证据。
// SECTION:dependency-closure
function dependencyClosure(roots, allAssets) {
const byId = new Map(allAssets.map((asset) => [asset.id, asset]));
const result = new Map(roots.map((asset) => [asset.id, asset]));
for (const asset of result.values()) {
for (const dependencyId of asset.dependencies) {
const dependency = byId.get(dependencyId);
if (dependency) result.set(dependency.id, dependency);
}
}
return [...result.values()];
}
报告是交付物
输入版本、工具版本和 rollback token 一起记录。
// SECTION:report
export function buildReport(records, inputVersion, toolVersion) {
return {
inputVersion,
toolVersion,
passed: records.filter((record) => record.status === 'pass').length,
failed: records.filter((record) => record.status === 'fail').length,
fixable: records.filter((record) => record.fixable).length,
rollbackToken: hash(records),
};
}
失败时能撤销
回滚依赖 token,而不是猜文件名。
// SECTION:rollback
export function rollback(report, store) {
if (!report.rollbackToken) throw new Error('missing rollback token');
return store.restore(report.rollbackToken);
}