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"}