mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 07:51:57 +00:00
ce71f7d5c1
复核第 1 轮(abb2707)找到四处:写作目录按证据面板宁可多遮的那一套认指标出处,SQL 认不准时没碰号码列的销售额也成了
「已遮罩」,正式运行引用它就判失败(相对基线回退);「紧挨着负号」连破折号「——」「—」和汉字「一」都算,「运费统一10元」
「合计——7,036.50元」原样导出变红(相对基线回退);紧挨着改字只堵住了数字、负号、小数点,接「万」、画删除线、从右往左
书写的字母、l / I / O 照样全绿,根子在总览把无法核对的项也给绿色;第二张口径卡转抄遮罩指标(nodes.<卡>.metrics[0].value、
vars.<assign_to>.metrics[0].value、nodes.<卡>.text)时号码照样进写作目录、文档证据目录和可验证报告。
取自遮罩格的指标只按确定的那一套认(写作目录、报告自查、可验证报告同一个判断)
- 写作目录(api/evidence.py 的 withheld_metrics)认指标出处时遮罩列只认确定取自遮罩列的(_InputMasks 的 sure:读得出来的
快照按单元格引用自查的 _cited_masks,读不出来的按数据源现在设的列名),和 [[v:…]] 引用同一格的自查同一个口径:WITH 写了
列名表、VALUES 当表、子查询里有没起别名的号码表达式、只装着分档标签的列,指标照常显示、照常引用;改了名字、套了函数的
号码列照旧「已遮罩」。证据面板、推演照旧宁可多遮。报告自查按目录里记的 masked 认,跟着同一个判断
- 可验证报告认老文档(证据目录里还写着原值,整份只给哈希)按同一个判断:api/bundle.py 先按证据面板的那一套认好每份查询
快照里确定取自遮罩列的列和这次运行查到过的遮罩值(certain_masks),交给 build_bundle(sure_columns、sure_secrets),
_Store.metric_word 的 sure 按它们认。认口径卡能不能随文件附带照旧宁可多遮(卡上记着输入的值),SQL 认不准的卡同基线只给 id
转抄上游口径卡的指标按上游那个指标认
- recompute.card_input 认出输入转抄了哪张卡的哪几个指标:nodes.<卡>.metrics[i].value、vars.<卡的 assign_to>.metrics[i]、
整个指标、输入、渲染算转抄那个指标;整张卡、清单文字、整份指标列表、下标不是字面量的同整份查询结果(值里有遮罩值才不给,
数指标个数的照给);只取名称、单位、口径名的不算
- 证据面板 / 写作目录(_InputMasks._copied)、可验证报告(_Store._copied_kind)都用同一个 _metric_masks / metric_word 往上追,
上游取自遮罩格,转抄的也不给;追了 CARD_DEPTH(8)张还没追到头(循环里抄自己上一轮的产出)的宁可多遮
- 写作目录读快照前把运行状态里每张口径卡的输入一并读好,不在事件循环里读盘
显示文字核对(verifier.js)
- 判不通过只剩紧挨着的数字、小数点,和真当负号用的三个(半角连字符减号、数学减号、全角减号)紧挨着数字开头、前面又不是
字母数字时(「SKU-12」「A-3」的连字符是编号的一部分)。破折号(——、—、–、―,小号的、竖排的)、波浪线、汉字「一」、
假名长音、制表符横线这些易混字当普通的字。连字符、不断行连字符、上下标负号、粗减号这类平时不当负号用的短横紧挨着数字
开头判无法核对(以前判不通过;基线也是无法核对)
- 判无法核对(带原因):后面接着量级单位(万、亿、千、百、兆、萬、億、%、‰、‱、k、K、M、B 及全角,中间隔着空格也算;
核对过的字以「万」「亿」结尾再接一个也算),核对过的字画着删除线或紧挨着画了删除线的字,紧挨着长得像数字的字母和数字
符号(l、I、O、上下标数字、圈码、罗马数字、汉字〇),从右往左书写的字母和方向控制字符(照旧)
- 总览(overallOf):一项无法核对都没有、每份报告都核对上才是绿色的「全部通过」;有无法核对的写中性的「可核对的项全部通过,
另有 N 项无法核对」,下面醒目地写明这个文件判断不了真伪,要在系统中核对封存(运行 ID、GET /api/runs/<id>/verify、定时任务
通知里的清单哈希前 12 位)。以前无法核对的项照样给绿色,伪造者只要让读者看到的那个数落进无法核对就行
- 交出 layoutOf 给 node 里逐条核排法;开发数据 39 份报告 1591 处、验收数据 58 份报告 1695 处引用按新规则扫过,没有一处判
不通过或无法核对
测试
- 新增 tests/test_report_masked_metric_sure.py(四种 SQL 认不准的销售额、分档标签照给照引、确定取自号码列的照旧遮、老文档
照常附带、写作目录和可验证报告逐个指标同一个判断)、tests/test_report_masked_card_copies.py(三种转抄写法在写作目录、文档、
自查、证据面板、可验证报告里都不给号码,没有报告的运行、老文档,转抄普通指标照给,认转抄的各种写法,追不到头的宁可多遮)、
tests/test_bundle_prose_export.py(「统一」「——」「—」「SKU-」「共…家」「–」真跑报告撰写节点、从接口导出,全部通过、总览绿)
- test_bundle_shown:负号只认三个、前面是汉字的照判不通过;像负号的短横、量级单位、删除线、长得像数字的字母各自判无法核对、
总览中性;正常写法原样导出全绿;总览只在一项无法核对都没有时是绿的
- 样本页新增 prose(写作者自己写在引用旁边的普通文字,原样导出全绿);check-bundle 补数学减号、紧挨着汉字加负号判不通过,
接万(隔空格)、接亿元、接 %、接 k、订单号接万、画删除线另写一个数、前面加小写 L、后面接大写 O、长得像负号的连字符判无法
核对,各自点名原因、总览中性并写明去系统里核对;有无法核对项的样本页(base、legacy、continued、missing-card、masked-card、
masked-source、nan)总览改断言中性,从右往左书写的字母那条不再断言总览是绿的
- 每处改动都做了「去掉实现、测试变红」:写作目录不用 sure、老文档不用 sure、sure 列不用、转抄认不出(三处各自)、追不到头
按核对不了、负号的字母数字例外、负号恢复成 \p{Pd} 全部、数学减号全角减号不算、量级单位、隔空格、以万结尾、删除线、像数字
的字母、像负号的短横、总览有无法核对照样绿
185 lines
13 KiB
HTML
185 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src __BUNDLE_CSP__; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>__BUNDLE_TITLE__</title>
|
||
<style>
|
||
:root {
|
||
color-scheme: light dark;
|
||
--bg: #ffffff; --panel: #f5f6f8; --raise: #ffffff; --border: #dfe2e8; --fg: #16181d; --dim: #41454e; --faint: #5f6470;
|
||
--accent: #2a57c2; --ok: #1b7339; --ok-soft: #e5f4ea; --bad: #b92424; --bad-soft: #fcebeb; --na: #565b66; --na-soft: #eceef2;
|
||
--mark: #fff1b8; --code: #eff1f5; --shadow: 0 10px 32px rgba(16, 20, 28, .16);
|
||
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
||
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
||
--detail-w: 420px;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--bg: #111318; --panel: #181b21; --raise: #1c1f26; --border: #2e323b; --fg: #e8eaee; --dim: #c1c5ce; --faint: #969ca8;
|
||
--accent: #8aaeff; --ok: #62d08c; --ok-soft: #14301f; --bad: #ff8080; --bad-soft: #3c1c1f; --na: #a9afbb; --na-soft: #262a31;
|
||
--mark: #4d4214; --code: #23262e; --shadow: 0 10px 32px rgba(0, 0, 0, .55);
|
||
}
|
||
}
|
||
* { box-sizing: border-box; }
|
||
[hidden] { display: none !important; }
|
||
/* 核对脚本一开始执行就给 html 标上 data-js:没执行(禁用脚本、附件预览)时只显示「尚未核对」那一句 */
|
||
html:not([data-js]) .js-only, html[data-js] .no-js { display: none !important; }
|
||
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
||
body { margin: 0; background: var(--bg); color: var(--fg); font: 15px/1.65 var(--sans); overflow-wrap: break-word; }
|
||
code, pre, .mono { font-family: var(--mono); }
|
||
.page { max-width: 920px; margin: 0 auto; padding: 28px 16px 72px; }
|
||
.brand { margin: 0; font-size: 13px; font-weight: 600; letter-spacing: .04em; color: var(--accent); }
|
||
h1 { margin: 2px 0 0; font-size: 24px; line-height: 1.35; }
|
||
.overall { display: flex; align-items: center; gap: 10px; margin: 16px 0 4px; padding: 12px 14px; border-radius: 10px;
|
||
border: 1px solid var(--border); background: var(--panel); font-size: 18px; font-weight: 650; }
|
||
.overall[data-overall="pass"] { color: var(--ok); background: var(--ok-soft); border-color: transparent; }
|
||
.overall[data-overall="fail"] { color: var(--bad); background: var(--bad-soft); border-color: transparent; }
|
||
.overall[data-overall="incomplete"], .overall[data-overall="partial"] { color: var(--na); background: var(--na-soft); border-color: var(--na); }
|
||
.overall-mark { font-size: 20px; line-height: 1; }
|
||
.overall-sub { margin: 4px 2px 0; font-size: 13px; color: var(--dim); }
|
||
/* 总览不是绿色的通过时:真伪以系统里的封存核对为准,写在总览正下方,比封存说明醒目 */
|
||
.overall-hint { margin: 8px 0 0; padding: 8px 10px; border-left: 3px solid var(--accent); border-radius: 6px; background: var(--panel);
|
||
color: var(--fg); font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
|
||
.seal { margin: 18px 0; padding: 14px 16px; border: 2px solid var(--accent); border-radius: 10px; }
|
||
.seal h2 { margin: 0 0 6px; font-size: 13px; font-weight: 600; color: var(--dim); }
|
||
.hash { display: block; font-size: 15px; font-weight: 600; line-height: 1.5; word-break: break-all; color: var(--fg); }
|
||
.seal p { margin: 10px 0 0; font-size: 13px; color: var(--dim); }
|
||
.seal p code { word-break: break-all; }
|
||
.seal .seal-warn { padding: 8px 10px; border-radius: 8px; background: var(--bad-soft); color: var(--bad); }
|
||
.meta { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 16px; margin: 0; font-size: 13px; }
|
||
.meta dt { color: var(--faint); }
|
||
.meta dd { margin: 0; min-width: 0; color: var(--dim); overflow-wrap: anywhere; }
|
||
.meta-note { margin: 14px 0 6px; font-size: 12px; color: var(--faint); }
|
||
.block { margin-top: 36px; }
|
||
.block > h2 { margin: 0 0 10px; font-size: 18px; }
|
||
.muted { color: var(--faint); }
|
||
.bad { color: var(--bad); }
|
||
.note { margin: 6px 0 12px; font-size: 13px; color: var(--dim); }
|
||
.warn { margin: 0 0 10px; padding: 8px 10px; border-radius: 8px; background: var(--bad-soft); color: var(--bad); font-size: 13px; }
|
||
.warn[data-why="missing"], .warn[data-why="unreadable"], .warn[data-why="masked"] { background: var(--na-soft); color: var(--na); }
|
||
.report { margin: 14px 0; padding: 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--raise); }
|
||
.report-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-bottom: 10px; }
|
||
.report-head h3 { margin: 0; font-size: 15px; }
|
||
.report-head .mono { font-size: 12px; }
|
||
.report-parts { flex: 1 1 100%; font-size: 12px; color: var(--dim); }
|
||
.doc { font-size: 15px; }
|
||
.doc p { margin: 8px 0; }
|
||
.doc .doc-h { margin: 18px 0 6px; line-height: 1.4; }
|
||
.doc .doc-h1 { font-size: 19px; } .doc .doc-h2 { font-size: 17px; } .doc .doc-h3 { font-size: 15px; }
|
||
.doc ul, .doc ol { margin: 8px 0; padding-left: 1.6em; }
|
||
.doc li { margin: 2px 0; }
|
||
.doc blockquote { margin: 8px 0; padding-left: 12px; border-left: 3px solid var(--accent); color: var(--dim); }
|
||
.doc pre { margin: 8px 0; padding: 10px 12px; border-radius: 8px; background: var(--code); overflow-x: auto; font-size: 13px; line-height: 1.55; }
|
||
.doc code { font-size: .92em; padding: 0 4px; border-radius: 4px; background: var(--code); }
|
||
.doc pre code { padding: 0; background: none; }
|
||
.doc hr { border: 0; border-top: 1px solid var(--border); margin: 14px 0; }
|
||
.doc .link { color: var(--accent); }
|
||
.table-wrap { max-width: 100%; margin: 8px 0; overflow-x: auto; border: 1px solid var(--border); border-radius: 8px; }
|
||
.table-wrap table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.table-wrap th, .table-wrap td { padding: 5px 9px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
|
||
.table-wrap th { background: var(--panel); color: var(--dim); font-weight: 600; white-space: nowrap; }
|
||
.table-wrap tr:last-child td { border-bottom: 0; }
|
||
.table-wrap .n { text-align: right; font-variant-numeric: tabular-nums; }
|
||
.table-wrap .hit { background: var(--mark); }
|
||
.num { font: inherit; color: inherit; background: none; border: 0; margin: 0; padding: 0 1px; border-radius: 3px; cursor: pointer;
|
||
text-decoration-line: underline; text-decoration-thickness: 1.5px; text-underline-offset: 3px; }
|
||
.num[data-result="pass"] { text-decoration-style: solid; text-decoration-color: var(--ok); }
|
||
.num[data-result="fail"] { text-decoration-style: wavy; text-decoration-color: var(--bad); background: var(--bad-soft); }
|
||
.num[data-result="na"] { text-decoration-style: dotted; text-decoration-color: var(--na); }
|
||
.num:hover { background: var(--panel); }
|
||
.num:focus-visible, .close:focus-visible, .items button:focus-visible, summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||
.num.is-active { background: var(--mark); }
|
||
.badge { display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 999px; font-size: 12px;
|
||
font-weight: 600; line-height: 1.5; white-space: nowrap; }
|
||
.badge[data-result="pass"] { color: var(--ok); background: var(--ok-soft); }
|
||
.badge[data-result="fail"] { color: var(--bad); background: var(--bad-soft); }
|
||
.badge[data-result="na"], .badge[data-result="partial"] { color: var(--na); background: var(--na-soft); }
|
||
.check { margin: 12px 0; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
|
||
.check-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 10px 12px; background: var(--panel); }
|
||
.check-head h3 { margin: 0; font-size: 14px; }
|
||
.check-sum { font-size: 13px; color: var(--dim); }
|
||
.items { margin: 0; padding: 0; list-style: none; }
|
||
.items li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; padding: 7px 12px; border-top: 1px solid var(--border); font-size: 13px; }
|
||
.items .what { flex: 1 1 220px; min-width: 0; overflow-wrap: anywhere; }
|
||
.items .why { flex: 1 1 100%; color: var(--dim); }
|
||
.items button { font: inherit; color: var(--accent); background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
|
||
details > summary { padding: 8px 12px; border-top: 1px solid var(--border); font-size: 13px; color: var(--accent); cursor: pointer; }
|
||
.detail { position: fixed; top: 0; right: 0; bottom: 0; z-index: 10; width: min(var(--detail-w), 100vw); overflow-y: auto;
|
||
background: var(--raise); border-left: 1px solid var(--border); box-shadow: var(--shadow); }
|
||
.detail-head { position: sticky; top: 0; display: flex; align-items: flex-start; gap: 8px; padding: 14px 16px 10px;
|
||
background: var(--raise); border-bottom: 1px solid var(--border); }
|
||
.detail-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 17px; overflow-wrap: anywhere; }
|
||
.close { flex: none; width: 30px; height: 30px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
|
||
color: var(--dim); font: 18px/1 var(--sans); cursor: pointer; }
|
||
.detail-body { padding: 12px 16px 28px; font-size: 14px; }
|
||
.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 7px 12px; margin: 0 0 12px; }
|
||
.kv dt { color: var(--faint); font-size: 13px; }
|
||
.kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
|
||
.sql { margin: 6px 0 12px; padding: 8px 10px; border-radius: 8px; background: var(--code); font-size: 12px; line-height: 1.5;
|
||
white-space: pre-wrap; overflow-wrap: anywhere; }
|
||
.sub { margin: 14px 0 4px; font-size: 13px; color: var(--faint); font-weight: 600; }
|
||
.foot { margin-top: 40px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 12px; color: var(--faint); }
|
||
.foot p { margin: 4px 0; }
|
||
@media print {
|
||
.detail, .close, details > summary { display: none !important; }
|
||
.page, html.detail-open .page { max-width: none; margin: 0; padding: 0; }
|
||
.num { text-decoration-color: currentColor !important; background: none !important; }
|
||
}
|
||
/* 宽屏上出处面板停在右侧:打开时正文让出这一栏,表格里右对齐的数字不会被面板盖住 */
|
||
@media screen and (min-width: 761px) {
|
||
html.detail-open .page { margin-right: min(var(--detail-w), 100vw); }
|
||
}
|
||
@media (max-width: 760px) {
|
||
.detail { top: auto; left: 0; width: auto; max-height: 72vh; border-left: 0; border-top: 1px solid var(--border); border-radius: 14px 14px 0 0; }
|
||
}
|
||
@media (max-width: 520px) {
|
||
body { font-size: 14px; }
|
||
.page { padding: 20px 16px 56px; }
|
||
h1 { font-size: 20px; }
|
||
.overall { font-size: 16px; }
|
||
.hash { font-size: 13px; }
|
||
.meta { grid-template-columns: minmax(0, 1fr); gap: 0; }
|
||
.meta dt { margin-top: 6px; }
|
||
.report { padding: 12px; }
|
||
}
|
||
</style>
|
||
<script>__BUNDLE_SCRIPT__</script>
|
||
</head>
|
||
<body>
|
||
<div class="page">
|
||
<header>
|
||
<p class="brand">可验证报告</p>
|
||
<h1 id="title">__BUNDLE_NAME__</h1>
|
||
<div id="overall" class="overall" data-overall="running" role="status" aria-live="polite">
|
||
<span class="overall-mark js-only" aria-hidden="true">…</span><span id="overall-text"><span class="js-only">正在核对…</span><span class="no-js">尚未核对:需要在浏览器中打开本文件,并允许执行其中的核对脚本</span></span>
|
||
</div>
|
||
<p id="overall-sub" class="overall-sub"></p>
|
||
<p id="overall-hint" class="overall-hint" role="note" hidden></p>
|
||
<section class="seal" aria-labelledby="seal-label">
|
||
<h2 id="seal-label">封存清单哈希</h2>
|
||
<code id="seal-hash" class="hash">__BUNDLE_SEAL__</code>
|
||
<p id="seal-note">这个文件只证明内部一致:报告里的数字能追到口径卡或查询快照,文档、快照和运行记录彼此对得上。它不能单独证明这些记录出自系统、未被整体替换。核对真伪:在系统中打开这次运行(运行 ID <code class="run-id">__BUNDLE_RUN__</code>),确认「凭证详情」里的清单哈希与上面一致,并点「核对」确认事件记录与封存时一致(接口 GET /api/runs/<span class="run-id">__BUNDLE_RUN__</span>/verify)。工作流名、版本、起止时间和节点名称取自运行记录,不在封存范围内,需要在系统中一并对照。</p>
|
||
<p id="seal-failed" class="seal-warn"__BUNDLE_FAILED__>这次运行以失败结束:之后若在系统中继续运行,系统会重新封存,本文件的封存哈希将无法再与系统对照。</p>
|
||
</section>
|
||
<dl id="meta" class="meta"></dl>
|
||
<p id="record-note" class="meta-note" hidden></p>
|
||
<dl id="record" class="meta"></dl>
|
||
</header>
|
||
<main class="js-only">
|
||
<section id="reports" class="block" aria-labelledby="reports-title"><h2 id="reports-title">报告</h2></section>
|
||
<section id="checks" class="block" aria-labelledby="checks-title"><h2 id="checks-title">核对明细</h2></section>
|
||
<section id="appended" class="block" aria-labelledby="appended-title" hidden><h2 id="appended-title">封存之后追加的事件</h2></section>
|
||
</main>
|
||
<footer id="foot" class="foot js-only"></footer>
|
||
</div>
|
||
<aside id="detail" class="detail" role="dialog" aria-modal="false" aria-labelledby="detail-title" hidden>
|
||
<div class="detail-head"><h2 id="detail-title"></h2><button type="button" id="detail-close" class="close" aria-label="关闭">×</button></div>
|
||
<div id="detail-body" class="detail-body"></div>
|
||
</aside>
|
||
<script type="application/json" id="bundle-data">__BUNDLE_DATA__</script>
|
||
</body>
|
||
</html>
|