From 27ac11fa5fbcc7a1ebd3f499198349ff25c234f2 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Wed, 21 Oct 2026 15:49:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E9=AA=8C=E8=AF=81=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E7=9A=84=E6=A0=B8=E5=AF=B9=E9=A1=B5=E7=BB=99=E5=88=86=E6=9E=90?= =?UTF-8?q?=E7=BB=93=E8=AE=BA=E7=9A=84=E6=96=B9=E6=B3=95=E5=86=99=E4=B8=8A?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=8F=AB=E6=B3=95=EF=BC=9A=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E5=9C=A8=E9=9A=8F=E6=96=87=E4=BB=B6=E9=99=84=E5=B8=A6?= =?UTF-8?q?=E7=9A=84=E5=88=86=E6=9E=90=E7=BB=93=E8=AE=BA=E6=9D=A1=E7=9B=AE?= =?UTF-8?q?=E4=B8=8A=E5=B8=A6=20method=5Flabel=EF=BC=88contract.method=5Fl?= =?UTF-8?q?abel=EF=BC=8C=E5=92=8C=E5=AF=BC=E5=87=BA=E9=A1=B5=E5=90=8C?= =?UTF-8?q?=E4=B8=80=E4=BB=BD=EF=BC=89=EF=BC=8Cverifier.js=20=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E8=AF=A6=E6=83=85=E3=80=81=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E4=B8=8A=E7=9A=84=E5=8F=AF=E6=AF=94=E6=80=A7=E4=B8=A4=E5=A4=84?= =?UTF-8?q?=E3=80=8C=E6=96=B9=E6=B3=95=E4=B8=8E=E7=89=88=E6=9C=AC=E3=80=8D?= =?UTF-8?q?=E7=85=A7=E5=AF=BC=E5=87=BA=E9=A1=B5=E5=86=99=E3=80=8C=E5=8F=98?= =?UTF-8?q?=E5=8C=96=E6=8B=86=E8=A7=A3=EF=BC=88=E5=8F=AF=E5=8A=A0=EF=BC=89?= =?UTF-8?q?=20=C2=B7=20decompose.additive@1=E3=80=8D=EF=BC=8C=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=8F=AA=E6=98=BE=E7=A4=BA=E3=80=81=E4=B8=8D=E5=8F=A6?= =?UTF-8?q?=E8=B5=B7=E5=8F=AB=E6=B3=95=EF=BC=9B=E6=9D=A1=E7=9B=AE=E4=B8=8A?= =?UTF-8?q?=E6=B2=A1=E5=B8=A6=E5=8F=AB=E6=B3=95=E7=9A=84=E5=8F=AA=E5=86=99?= =?UTF-8?q?=20id=E3=80=82=E4=BB=A5=E5=89=8D=E5=8F=AA=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=96=B9=E6=B3=95=20id=E3=80=82=E6=B5=8B=E8=AF=95=EF=BC=9A?= =?UTF-8?q?=E5=88=86=E6=9E=90=E7=BB=93=E8=AE=BA=E3=80=81=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=E5=8D=A1=E6=8C=87=E6=A0=87=E4=B8=8A=E7=9A=84=E5=8F=AF=E6=AF=94?= =?UTF-8?q?=E6=80=A7=E4=B8=A4=E7=A7=8D=E6=9D=A1=E7=9B=AE=E9=83=BD=E5=B8=A6?= =?UTF-8?q?=E5=8F=AB=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/bundle/build.py | 8 +++++--- backend/app/bundle/verifier.js | 12 ++++++++++-- backend/tests/test_analysis_bundle.py | 4 +++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/backend/app/bundle/build.py b/backend/app/bundle/build.py index 259e20f1..ac8a10a9 100644 --- a/backend/app/bundle/build.py +++ b/backend/app/bundle/build.py @@ -63,7 +63,7 @@ from pathlib import Path from typing import Any from urllib.parse import quote -from app.analysis.contract import ARTIFACT_KIND, LEDGER_KIND, NODE_TYPE as ANALYSIS_NODE, is_result +from app.analysis.contract import ARTIFACT_KIND, LEDGER_KIND, NODE_TYPE as ANALYSIS_NODE, is_result, method_label from app.core.artifact_store import canonical_json, content_hash from app.core.events import EventType from app.engine.evidence import analysis_recheck, iter_units, query_entry_fields @@ -1161,8 +1161,9 @@ class _Artifacts: def _attach_analysis(artifacts: _Artifacts, store: _Store, item: dict[str, Any] | None, artifact: str, owner: str | None, cited_queries: list[str], depth: int = 0) -> None: """随文件附带的分析结论:它的输入快照(种类 query,遮罩照常认)、下钻的上一层结论(同样附带它的输入,最多 - _PARENT_DEPTH 层)一并列进清单,再把服务端的复算结论记在这份结论的条目上(recheck)。只给 id 的结论、被改过的 - (哈希对不上)不顺着它去附带别的:被改过的结论说输入是哪几份,不能替它圆。""" + _PARENT_DEPTH 层)一并列进清单,再把服务端的复算结论记在这份结论的条目上(recheck),连同方法的叫法(method_label, + contract.method_label,和导出页同一份:核对页只显示,不在页面里另起一套叫法)。只给 id 的结论、被改过的(哈希对不上) + 不顺着它去附带别的:被改过的结论说输入是哪几份,不能替它圆。""" if item is None or "b64" not in item or not store.intact(artifact) or "recheck" in item: return result = store.content(artifact) @@ -1176,6 +1177,7 @@ def _attach_analysis(artifacts: _Artifacts, store: _Store, item: dict[str, Any] continue artifacts.add(input_id, "query", owner) cited_queries.append(input_id) + item["method_label"] = method_label(result.get("op"), result.get("mode")) item["recheck"] = _recheck(result, store) diff --git a/backend/app/bundle/verifier.js b/backend/app/bundle/verifier.js index 90309994..767828fe 100644 --- a/backend/app/bundle/verifier.js +++ b/backend/app/bundle/verifier.js @@ -1983,6 +1983,14 @@ return parts } + /** 方法与版本那一行:服务端在工件条目上带的叫法(method_label,和导出页同一份「变化拆解(可加) · decompose.additive@1」) + * 加方法 id;条目上没带叫法的只写 id */ + function methodText(art, result) { + const label = art && isObj(art.a) && typeof art.a.method_label === 'string' ? art.a.method_label : '' + const id = String(result.method || '—') + return label ? `${label} · ${id}` : id + } + /** 口径卡指标上的可比性(口径卡执行时自动做的可比性检查):卡上记的判定和原因;那份结论随文件附带、核对通过的,再给 * 判定标准、方法与版本、各项检查和导出时服务端的复算。没附带的照卡上的记录写,说明为什么没有更多 */ function comparabilityBlock(c) { @@ -2002,7 +2010,7 @@ } parts.push(kv([ result.criterion ? [T.dCriterion, String(result.criterion)] : null, - [T.dMethod, String(result.method || '—')], + [T.dMethod, methodText(art, result)], ])) const table = isObj(result.table) ? result.table : null const columns = table && Array.isArray(table.columns) ? table.columns.map(String) : [] @@ -2030,7 +2038,7 @@ parts.push(kv([ [T.dPhrase, String(result.phrase || '—')], result.criterion ? [T.dCriterion, String(result.criterion)] : null, - [T.dMethod, String(result.method || '—')], + [T.dMethod, methodText(art, result)], ])) const caveats = (Array.isArray(result.caveats) ? result.caveats : []).filter(isObj).map((c) => String(c.text || '')) if (caveats.length) parts.push(h('p', { class: 'sub' }, T.dCaveats), h('ul', { class: 'items' }, caveats.map((c) => h('li', {}, c)))) diff --git a/backend/tests/test_analysis_bundle.py b/backend/tests/test_analysis_bundle.py index d41f31aa..6572040a 100644 --- a/backend/tests/test_analysis_bundle.py +++ b/backend/tests/test_analysis_bundle.py @@ -106,6 +106,8 @@ def test_the_analysis_and_its_input_travel_with_the_file_and_verify(): # 导出时服务端真的复算了一遍:按封存的输入快照(这份文件里哈希对得上的原字节)重算,逐字段相同 assert analysis_item["recheck"] == {"status": "same", "method": "decompose.additive@1", "text": "复算一致:按封存的输入重新计算,结论逐项相同"} + # 方法的叫法由服务端带上(和导出页同一份),核对页只显示「变化拆解(可加) · decompose.additive@1」 + assert analysis_item["method_label"] == "变化拆解(可加)" [query] = [x for x in bundle["artifacts"] if x["kind"] == "query"] assert "b64" in query @@ -259,7 +261,7 @@ def test_the_comparability_on_a_card_travels_with_the_card_and_is_chained_throug assert "b64" in item(bundle, card_id) comparability = item(bundle, found) assert comparability["kind"] == ARTIFACT_KIND and "b64" in comparability and comparability["node_id"] == "card" - assert comparability["recheck"]["status"] == "same" + assert comparability["recheck"]["status"] == "same" and comparability["method_label"] == "可比性检查" got = verdicts(bundle) assert all(r == "pass" for key in ("events", "manifest", "artifacts", "chain") for r in got[key]), got["why"] assert {w["kind"] for w in got["why"]["chain"]} >= {"metric_set", ARTIFACT_KIND, "query"}