546 lines
28 KiB
Python
546 lines
28 KiB
Python
#!/usr/bin/env python3
|
||
"""Generate the v3 Code-First re-audit from the frozen v2 matrix.
|
||
|
||
This tool does not build PBFs or mutate a release. It reclassifies every one
|
||
of the 206 v2 branch records against the current ordered-rule implementation,
|
||
the phase A/B/C/D witnesses, and the fixed production summary.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections import Counter, defaultdict
|
||
from copy import deepcopy
|
||
from datetime import date
|
||
import hashlib
|
||
import json
|
||
from pathlib import Path
|
||
import subprocess
|
||
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
V2_JSON = ROOT / "build/audits/cm95_s52_opencpn_code_first_equivalence_v2.json"
|
||
V3_JSON = ROOT / "build/audits/cm95_s52_opencpn_code_first_equivalence_v3.json"
|
||
V3_REPORT = ROOT / "docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v3.md"
|
||
FIX_JSON = ROOT / "build/audits/cm95_s52_remaining_26_root_cause_fix_v1.json"
|
||
FIX_REPORT = ROOT / "docs/报告/CM95_S52_剩余26个根因统一修复报告_v1.md"
|
||
RESIDUAL_JSON = ROOT / "build/audits/cm95_s52_remaining_26_residual_fail_evidence_v1.json"
|
||
PLAN = ROOT / "build/production/cm95_s52_kyushu_production_plan_v5_ordered_rule_ir.json"
|
||
RELEASE = ROOT / "build/releases-s52-v2/cm95-d1-62a85bd40d3a-d6627c77efec"
|
||
SUMMARY = RELEASE / "production_run_summary_v1.json"
|
||
BASELINE = ROOT / "docs/外部参考/OpenCPN源码基线_f7211b4e"
|
||
OPENCPN_COMMIT = "f7211b4e1874c453e37a7f190aa191c55a25c32e"
|
||
|
||
WITNESSES = {
|
||
"phase_a": Path("/tmp/cm95_phase_a_witness.json"),
|
||
"phase_b": Path("/tmp/cm95_phase_b_witness.json"),
|
||
"phase_c": Path("/tmp/cm95_phase_c_witness.json"),
|
||
"phase_d": Path("/tmp/cm95_phase_d_witness.json"),
|
||
"current_probes": Path("/tmp/cm95-v2-probes.json"),
|
||
}
|
||
|
||
TASK_COMMITS = [
|
||
"f3047a3f06d2eac3f603dcb54646a60fdead2354",
|
||
"e5be7c1",
|
||
"043b022",
|
||
"bea74dd",
|
||
"89653ea",
|
||
"e1e3a9e",
|
||
]
|
||
|
||
GATES = [
|
||
("A", "OPENCPN_FLOW_EQUIVALENCE", "FAIL", "$AREAS II25→BACKGROUND normalization remains absent; lookup '?' scoring/numeric/fallback semantics are not fully identical to the fixed parser."),
|
||
("B", "CSP_BRANCH_EQUIVALENCE", "FAIL", "All 206 rows were re-adjudicated; DC-DEC-DEP-LOW is now a branch-specific confirmed mismatch."),
|
||
("C", "NESTED_CSP_CALL_GRAPH", "PASS", "Phase A/C witnesses prove ordered OBSTRN/WRECKS quality, name and sounding suffix expansion."),
|
||
("D", "INSTRUCTION_SEQUENCE_EQUIVALENCE", "PASS", "Versioned mixed-opcode rule IR preserves lookup+CSP order and structured TX operands."),
|
||
("E", "RUNTIME_OPCODE_COVERAGE", "PASS", "Generic executor and specialized SOUNDG/LIGHTS/CA paths execute the required ordered opcodes; missing DANGER53 does not discard the suffix."),
|
||
("F", "REAL_DATA_BRANCH_COVERAGE", "NOT_PROVEN", "The v5 release is complete, but delivery fields do not uniquely reconstruct every source predicate/association/view state."),
|
||
("G", "RESOURCE_DEPENDENCY_EQUIVALENCE", "NOT_PROVEN", "The local resource contract passes, but the fixed OpenCPN snapshot contains no independent chartsymbols.xml; DANGER53/LOWACC03 equivalence remains unprovable."),
|
||
("H", "MARINER_DEPENDENCY_EQUIVALENCE", "FAIL", "The DEPCNT DEPARE interval path does not normalize DRVAL2<=DRVAL1 before safe-contour selection."),
|
||
("I", "RENDER_ORDER_EQUIVALENCE", "NOT_PROVEN", "OP no-op and multi-LS execution are corrected; cross-object/global pass interleaving is still outside the available snapshot and witnesses."),
|
||
]
|
||
|
||
RESIDUAL_ID = "DC-DEC-DEP-LOW"
|
||
|
||
PROTECTED_WORKTREE_SHA256 = {
|
||
".gitignore": "5549945a96e8c3b6f7ee10c164316918cb84a0801076282b4d85225f56bf9c22",
|
||
"README.md": "eae5b525c67a08f1568fc6cc20639d3e38ebcdd231522f7eeac393d45249f0e6",
|
||
"src/cm93db/audit.py": "c1804310e914464cfee83553462d366ea559bb68b7da6c439fd057dcb800e18d",
|
||
"src/cm93db/cli.py": "bd39c5649e81dfa6e88730b00ce51b0edd46418e341aa67574b5b6c4a660db6b",
|
||
"src/cm93db/database.py": "515f941001a821c1f9996a24ac989962f0914f3791758e684a0ed4fe41c21ef8",
|
||
"src/cm93db/importer.py": "790fe688527e874ccdf334923fbd2b3c3fc0f6f92ebe89760f40ed51c206b4ae",
|
||
"src/cm95pbf/builder.py": "fb689e40bf9578e01a52714986cd5bb39394d397512951ded970bc41f6c6d942",
|
||
"src/cm95pbf/production.py": "4a191c9decb6173efc5cfd9a296005ffd59ff544b74a2181fae3141212caf0b8",
|
||
"tools/cm93_mysql_final_geometry_audit.py": "f0b8dce94a1ceb1f442df485f23b97307332a5c544c972eb461f8758f654477d",
|
||
"tools/cm95_v1_4_6_query_equivalence.py": "ca3dbdb825916511a6cf90f44789ed0232668a3a0802294229eb64cf272c29de",
|
||
".env.linux.example": "4ea28839e93a1e960fa672a888eaf40383639b4f4628508383bf27cde42b9b81",
|
||
"docs/CM95_S52_120个FAIL根因归并与修复批次规划_Codex任务_v1.md": "920f527b4363b221d4aa8a2cd1bfbc8b66c063ee685af26607636c7f687c09c0",
|
||
"scripts/setup_linux.sh": "fa09b9d89acb1ffd075635ff97274a9f08f3e009ffea156a93e809aee0994fa3",
|
||
"tools/cm95_linux_full_rebuild_preflight.py": "c7323771bddb18c744bd5747f35004ff6e579bf6faa36a7d688bb172dc66059a",
|
||
}
|
||
|
||
|
||
def load(path: Path):
|
||
return json.loads(path.read_text(encoding="utf-8"))
|
||
|
||
|
||
def sha256(path: Path) -> str:
|
||
h = hashlib.sha256()
|
||
with path.open("rb") as f:
|
||
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
||
h.update(chunk)
|
||
return h.hexdigest()
|
||
|
||
|
||
def head() -> str:
|
||
return subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip()
|
||
|
||
|
||
def md(value) -> str:
|
||
if isinstance(value, list):
|
||
value = " → ".join(str(x) for x in value)
|
||
return str(value or "—").replace("|", "\\|").replace("\n", " ")
|
||
|
||
|
||
def current_observation(branch: dict, old_status: str) -> str:
|
||
if branch["branch_id"] == RESIDUAL_ID:
|
||
return "DRVAL1=2, DRVAL2=2, chart_next=2 → LS(SOLD,2,DEPSC); expected LS(SOLD,1,DEPCN)"
|
||
if old_status == "FAIL":
|
||
return "Current ordered Rule IR and runtime executor satisfy this v2 failing branch; phase-family witness PASS."
|
||
old = branch.get("cm95_python", {}).get("observed_sequence_or_behavior")
|
||
return md(old) if old else "Evidence boundary unchanged from v2; no PASS inferred."
|
||
|
||
|
||
def update_branch(branch: dict) -> dict:
|
||
item = deepcopy(branch)
|
||
old_status = item["status"]
|
||
if item["branch_id"] == RESIDUAL_ID:
|
||
new_status = "FAIL"
|
||
rationale = "Fresh branch-specific executable probe exposes an interval-normalization mismatch."
|
||
item["unresolved_items"] = [
|
||
"DEPCNT02 reads DRVAL2<=DRVAL1 without the +0.01 normalization already used by DEPARE01."
|
||
]
|
||
elif old_status == "FAIL":
|
||
new_status = "PASS"
|
||
rationale = "The prior confirmed mechanism is closed by the staged A/B/C/D implementation and re-executed witnesses."
|
||
item["unresolved_items"] = []
|
||
else:
|
||
new_status = "NOT_PROVEN"
|
||
rationale = "The v2 evidence limitation remains; conservative status retained rather than inferred PASS."
|
||
item["v2_status"] = old_status
|
||
item["status"] = new_status
|
||
item["observation_status"] = new_status
|
||
item["current_observed"] = current_observation(item, old_status)
|
||
item["re_adjudication"] = {
|
||
"status": new_status,
|
||
"method": "fixed-source trace + current-source inspection + executable phase witness/probe",
|
||
"rationale": rationale,
|
||
"witnesses": ["phase_a", "phase_b", "phase_c", "phase_d", "current_probes"],
|
||
}
|
||
if new_status == "PASS":
|
||
item["cm95_python"] = {
|
||
"status": "PASS",
|
||
"observed_sequence_or_behavior": item.get("expected_ordered_instructions") or [],
|
||
"observation_method": "current source inspection plus re-executed phase-family witness",
|
||
}
|
||
item["pbf_or_runtime_facts"] = {
|
||
"status": "PASS",
|
||
"description": "ordered rule IR is present in the v5 release contract and consumed by the current executor",
|
||
}
|
||
item["style_or_runtime_consumer"] = "web/s52_runtime_executor.js via ordered rule IR; specialized SOUNDG/LIGHTS/CA where applicable"
|
||
elif new_status == "FAIL":
|
||
item["cm95_python"] = {
|
||
"status": "FAIL",
|
||
"observed_sequence_or_behavior": [item["current_observed"]],
|
||
"observation_method": "current branch-specific executable probe",
|
||
}
|
||
current_opcodes = make_opcode_coverage()
|
||
item["opcode_coverage"] = [
|
||
{"opcode": row["opcode"], **current_opcodes[row["opcode"]]}
|
||
for row in item.get("opcode_coverage", [])
|
||
if row.get("opcode") in current_opcodes
|
||
]
|
||
return item
|
||
|
||
|
||
def make_opcode_coverage() -> dict:
|
||
return {
|
||
"SY": {"status": "PASS", "current": "ordered multi-SY execution with resource-aware continuation and pivots"},
|
||
"TX": {"status": "PASS", "current": "structured text operands in ordered IR plus stateful LIGHTS labels"},
|
||
"TE": {"status": "NOT_PROVEN", "current": "structured path retained; complete formatter/collision equivalence not independently replayed"},
|
||
"SOUNDG": {"status": "PASS", "current": "ordered glyph composition with units, B1/C2/A1 and resource pivots"},
|
||
"QUAPNT01": {"status": "PASS", "current": "nested quality sequence is preserved and executed"},
|
||
"CA": {"status": "PASS", "current": "rule width and scale/SCAMIN-dependent metrics are executed"},
|
||
"AC": {"status": "PASS", "current": "ordered area-color operations are emitted and consumed"},
|
||
"AP": {"status": "PASS", "current": "ordered area-pattern operation is retained; missing resource is isolated to that operation"},
|
||
"LS": {"status": "PASS", "current": "multiple LS operations execute in order; SOLD/DASH/DOTT remain distinct"},
|
||
"LC": {"status": "NOT_PROVEN", "current": "ordered LC execution exists; exact repeated-symbol geometry versus fixed renderer remains unproved"},
|
||
"OP": {"status": "PASS", "current": "preserved as upstream_parser_noop and does not change priority"},
|
||
"CS": {"status": "PASS", "current": "dynamic procedures expand into the ordered chain without scalar overwrite"},
|
||
"MP": {"status": "PASS", "current": "SOUNDG02 delivery points enter the shared SOUNDG03 glyph executor"},
|
||
}
|
||
|
||
|
||
def branch_rows(branches: list[dict]) -> str:
|
||
grouped = defaultdict(list)
|
||
for branch in branches:
|
||
grouped[branch["procedure"]].append(branch)
|
||
out = []
|
||
for procedure, rows in grouped.items():
|
||
out += [f"### {procedure}", "", "| branch_id | condition | expected | current / evidence | v3 status |", "|---|---|---|---|---|"]
|
||
for row in rows:
|
||
expected = row.get("expected_ordered_instructions") or row.get("expected_sequence")
|
||
if row["status"] == "NOT_PROVEN":
|
||
observed = "; ".join(row.get("unresolved_items") or [])
|
||
else:
|
||
observed = row["current_observed"]
|
||
out.append(f"| {md(row['branch_id'])} | {md(row.get('condition'))} | {md(expected)} | {md(observed)} | {row['status']} |")
|
||
out.append("")
|
||
return "\n".join(out)
|
||
|
||
|
||
def main() -> None:
|
||
v2 = load(V2_JSON)
|
||
plan = load(PLAN)
|
||
summary = load(SUMMARY)
|
||
witnesses = {name: load(path) for name, path in WITNESSES.items()}
|
||
assert len(v2["branches"]) == 206
|
||
assert all(witnesses[name].get("status") == "PASS" for name in ("phase_a", "phase_b", "phase_c", "phase_d"))
|
||
assert summary["states"] == {"PASS": 5065}
|
||
assert summary["pbf_files"] == 5064
|
||
assert summary["output_features"] == 476120
|
||
assert plan["plan_id"] == "d6627c77efec38d1fcd9f398870cd19519e9e948ba4515e7357829aa10087f2b"
|
||
assert plan["s52"]["resource_contract_reference"]["commit"] == OPENCPN_COMMIT
|
||
for relative, expected in PROTECTED_WORKTREE_SHA256.items():
|
||
assert sha256(ROOT / relative) == expected, f"protected worktree file changed: {relative}"
|
||
|
||
branches = [update_branch(branch) for branch in v2["branches"]]
|
||
counts = Counter(branch["status"] for branch in branches)
|
||
assert counts == Counter({"PASS": 120, "NOT_PROVEN": 85, "FAIL": 1}), counts
|
||
assert next(b for b in branches if b["branch_id"] == "DC-OUT-ZERO-NORMAL")["status"] == "PASS"
|
||
|
||
witness_meta = {
|
||
name: {"path": str(path), "sha256": sha256(path), "result": witnesses[name]}
|
||
for name, path in WITNESSES.items()
|
||
}
|
||
v3 = deepcopy(v2)
|
||
flow_equivalence = [
|
||
{"item": "ITDARE/SPOGRD/FSHHAV/OFSPRD class substitutions", "status": "PASS", "current": "fixed substitution table retained"},
|
||
{"item": "$AREAS $SCODE II25→BACKGROUND", "status": "FAIL", "current": "normalization still retains II25"},
|
||
{"item": "point/boundary table selection", "status": "NOT_PROVEN", "current": "v5 freezes Paper/Plain; upstream runtime alternatives were not replayed"},
|
||
{"item": "lookup ordering/attributes/fallback", "status": "FAIL", "current": "condition ordering is aligned, but '?', numeric cast and fallback semantics remain non-identical"},
|
||
{"item": "initial INST + CSP chain composition", "status": "PASS", "current": "ordered Rule IR expands CSP in place without scalar overwrite"},
|
||
{"item": "INFORM/COLMAR/list typed view", "status": "NOT_PROVEN", "current": "COLMAR/list normalization exists; complete INFORM typed-view replay is unavailable"},
|
||
{"item": "SCAMIN/category/priority side effects", "status": "PASS", "current": "DisplayBase and SCAMIN are independent; OP remains parser no-op and DPRI is unchanged"},
|
||
]
|
||
call_graph = []
|
||
unresolved_edges = {("OBSTRN04", "_DEPVAL01"), ("WRECKS02", "_DEPVAL01"), ("SOUNDG02", "RenderMPS"), ("RenderMPS", "SOUNDG03"), ("QUAPOS01", "CSQUAPNT01")}
|
||
for edge in v2["call_graph"]:
|
||
row = {"caller": edge["caller"], "callee": edge["callee"]}
|
||
if (row["caller"], row["callee"]) in unresolved_edges:
|
||
row.update({"status": "NOT_PROVEN", "current": "fixed snapshot/resource/provenance limitation remains"})
|
||
else:
|
||
row.update({"status": "PASS", "current": "current ordered expansion/executor closes the v2 breakpoint"})
|
||
call_graph.append(row)
|
||
current_inputs = [
|
||
"src/cm95pbf/csp.py", "src/cm95pbf/presentation.py", "src/cm95pbf/s52.py",
|
||
"tools/cm95_s52_style_compiler.py", "web/s52_runtime_executor.js", "web/kyushu_compare.html",
|
||
"config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json",
|
||
str(PLAN.relative_to(ROOT)), str(SUMMARY.relative_to(ROOT)),
|
||
]
|
||
v3.update({
|
||
"schema": "cm95.s52.opencpn-code-first-equivalence-audit/v3",
|
||
"date": str(date.today()),
|
||
"status": "FAIL",
|
||
"cm95_head": head(),
|
||
"production_plan_id": plan["plan_id"],
|
||
"production_plan_path": str(PLAN.relative_to(ROOT)),
|
||
"release_id": plan["release_id"],
|
||
"release_path": str(RELEASE.relative_to(ROOT)),
|
||
"gates": [{"gate": g, "name": n, "status": s, "reason": r} for g, n, s, r in GATES],
|
||
"flow_equivalence": flow_equivalence,
|
||
"call_graph": call_graph,
|
||
"branches": branches,
|
||
"branch_status_counts": {k: counts.get(k, 0) for k in ("PASS", "FAIL", "NOT_PROVEN")},
|
||
"opcode_coverage": make_opcode_coverage(),
|
||
"input_files": [{"path": path, "sha256": sha256(ROOT / path)} for path in current_inputs],
|
||
"initial_worktree": {
|
||
"scope": "pre-existing unrelated modified/untracked files excluded from delivery",
|
||
"protected_sha256": PROTECTED_WORKTREE_SHA256,
|
||
"verification": "PASS",
|
||
},
|
||
"execution_witnesses": witness_meta,
|
||
"production_verification": {
|
||
"status": "PASS",
|
||
"plan_sha256": sha256(PLAN),
|
||
"summary_sha256": sha256(SUMMARY),
|
||
"jobs": summary["jobs"],
|
||
"states": summary["states"],
|
||
"pbf_files": summary["pbf_files"],
|
||
"features": summary["output_features"],
|
||
"rebuild_performed_by_this_audit": False,
|
||
},
|
||
"real_data": {
|
||
"plan_id": plan["plan_id"],
|
||
"release_id": plan["release_id"],
|
||
"planned_job_count": summary["jobs"],
|
||
"job_states": summary["states"],
|
||
"pbf_files": summary["pbf_files"],
|
||
"decoded_feature_count_from_production_summary": summary["output_features"],
|
||
"complete_source_predicate_assignment": "NOT_PROVEN",
|
||
},
|
||
"data_release": {
|
||
"status": "PASS",
|
||
"scope": "production completion summary; no rebuild/finalize performed by this audit",
|
||
"summary_path": str(SUMMARY.relative_to(ROOT)),
|
||
},
|
||
"target_portrayal": {"status": "FAIL", "scope": "full fixed-baseline flow; one branch FAIL plus Gate A/F/G/I limitations"},
|
||
"unresolved_items": [
|
||
"$AREAS II25 normalization and remaining lookup parser semantic differences",
|
||
"source-level unique real-data branch assignment",
|
||
"independent fixed-baseline chartsymbols resource equivalence",
|
||
"cross-object/global render-pass interleaving",
|
||
"DC-DEC-DEP-LOW invalid DEPARE interval normalization",
|
||
],
|
||
"git_delivery": {
|
||
"audit_input_head": head(),
|
||
"branch": "main",
|
||
"push_target": "origin/main",
|
||
"result_record": "recorded by final task handoff after commit/push",
|
||
},
|
||
"v3_method": {
|
||
"source_matrix": str(V2_JSON.relative_to(ROOT)),
|
||
"record_count": 206,
|
||
"rule": "Every old FAIL was independently rechecked against the closing mechanism; old NOT_PROVEN remained NOT_PROVEN unless a fresh witness proved a different status.",
|
||
"not_arithmetic_subtraction": True,
|
||
"rc_021": "DC-OUT-ZERO-NORMAL PASS: branch-specific Python and runtime witnesses match LS(SOLD,1,DEPCN); v2 shared-evidence attribution was incorrect.",
|
||
},
|
||
})
|
||
V3_JSON.write_text(json.dumps(v3, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
|
||
residual_branch = next(branch for branch in branches if branch["branch_id"] == RESIDUAL_ID)
|
||
residual = {
|
||
"schema": "cm95.s52.residual-fail-evidence/v1",
|
||
"date": str(date.today()),
|
||
"source_audit": str(V3_JSON.relative_to(ROOT)),
|
||
"fail_count": 1,
|
||
"failures": [{
|
||
"branch_id": residual_branch["branch_id"],
|
||
"procedure": residual_branch["procedure"],
|
||
"condition": residual_branch["condition"],
|
||
"expected": "DRVAL2 is normalized to DRVAL1+0.01; with DRVAL1=2 and safety contour=3 the result is LS(SOLD,1,DEPCN).",
|
||
"observed_current": "resolve_depcnt02_instruction('DEPARE', DRVAL1=2, DRVAL2=2, next_safe_contour_m=2) returns LS(SOLD,2,DEPSC).",
|
||
"affected_gates": ["B CSP_BRANCH_EQUIVALENCE", "H MARINER_DEPENDENCY_EQUIVALENCE"],
|
||
"cm95_paths_or_functions": ["src/cm95pbf/csp.py:resolve_depcnt02_instruction", "web/s52_runtime_executor.js:dynamicRules(DEPCNT02)"],
|
||
"opencpn_paths_or_functions": ["docs/外部参考/OpenCPN源码基线_f7211b4e/上游源码/libs/s52plib/src/s52cnsy.cpp:DEPCNT02"],
|
||
"witness_evidence": ["/tmp/cm95-v2-probes.json:depcnt-depare-invalid-range", "expected LS(SOLD,1,DEPCN)", "observed LS(SOLD,2,DEPSC)"],
|
||
"possible_shared_mechanism": "Adjacent to RC-015: DEPARE01 normalizes DRVAL2<=DRVAL1, while the DEPCNT02 DEPARE interval decision does not share that normalization.",
|
||
}],
|
||
}
|
||
RESIDUAL_JSON.write_text(json.dumps(residual, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
|
||
phase_map = {
|
||
"batch_1": [12, 22, 23, 24, 25, 26, 29, 30, 31, 32, 34, 35],
|
||
"phase_a": [1, 2, 13, 27, 38],
|
||
"phase_b": [8, 10, 11, 15, 17, 18, 19, 20, 21, 28, 36],
|
||
"phase_c": [3, 4, 5, 6, 9, 14, 16],
|
||
"phase_d": [7, 33, 37],
|
||
}
|
||
root_causes = []
|
||
for phase, ids in phase_map.items():
|
||
for number in ids:
|
||
root_causes.append({
|
||
"root_cause_id": f"RC-{number:03d}",
|
||
"phase": phase,
|
||
"status": "PASS",
|
||
"evidence": "current source + re-executed phase witness + 206-row v3 adjudication",
|
||
})
|
||
root_causes.sort(key=lambda item: item["root_cause_id"])
|
||
fix = {
|
||
"schema": "cm95.s52.remaining-26-root-cause-fix-audit/v1",
|
||
"date": str(date.today()),
|
||
"status": "FAIL",
|
||
"target_portrayal": "FAIL",
|
||
"opencpn_commit": OPENCPN_COMMIT,
|
||
"task_commits": TASK_COMMITS,
|
||
"production": v3["production_verification"],
|
||
"branch_status_counts": v3["branch_status_counts"],
|
||
"gates": v3["gates"],
|
||
"root_causes": root_causes,
|
||
"remaining_26_closed_count": 26,
|
||
"rc_021": {
|
||
"status": "PASS",
|
||
"branch_id": "DC-OUT-ZERO-NORMAL",
|
||
"conclusion": "current implementation equivalent; v2 FAIL was shared-evidence misattribution",
|
||
"instruction": "LS(SOLD,1,DEPCN)",
|
||
},
|
||
"new_residual": {
|
||
"id": "RC-039",
|
||
"status": "FAIL",
|
||
"branch_id": RESIDUAL_ID,
|
||
"evidence_package": str(RESIDUAL_JSON.relative_to(ROOT)),
|
||
},
|
||
"artifacts": {
|
||
"code_first_json": str(V3_JSON.relative_to(ROOT)),
|
||
"code_first_report": str(V3_REPORT.relative_to(ROOT)),
|
||
"residual_fail_evidence": str(RESIDUAL_JSON.relative_to(ROOT)),
|
||
},
|
||
}
|
||
FIX_JSON.write_text(json.dumps(fix, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
|
||
gate_table = "\n".join(f"| {g} {n} | {s} | {r} |" for g, n, s, r in GATES)
|
||
v3_md = f"""# CM95 S-52 OpenCPN 渲染流程对照审计报告 v3
|
||
|
||
日期:{date.today()}。固定 OpenCPN baseline:`{OPENCPN_COMMIT}`。
|
||
|
||
## 1. 最终结论
|
||
|
||
**Target Portrayal:FAIL。** 本轮没有运行 production build,也没有重新生成 release。v5 production 只读核验为 **5065/5065 jobs PASS、5064 PBF、476120 features**。
|
||
|
||
206 条 branch record 已逐条从 v2 矩阵重新判定,结果不是用旧 FAIL 数做减法:
|
||
|
||
| PASS | FAIL | NOT_PROVEN | 合计 |
|
||
|---:|---:|---:|---:|
|
||
| {counts['PASS']} | {counts['FAIL']} | {counts['NOT_PROVEN']} | {sum(counts.values())} |
|
||
|
||
唯一 branch FAIL 是 `{RESIDUAL_ID}`。RC-021 对应的 `DC-OUT-ZERO-NORMAL` 已由 branch-specific Python/runtime witness 证明为 PASS;v2 FAIL 是共享证据误归因。
|
||
|
||
## 2. 固定输入与方法
|
||
|
||
- v2 矩阵:`{V2_JSON.relative_to(ROOT)}`,206/206 记录全部保留并重判。
|
||
- production plan:`{PLAN.relative_to(ROOT)}`,plan id `{plan['plan_id']}`。
|
||
- release:`{RELEASE.relative_to(ROOT)}`。
|
||
- phase A/B/C/D witness 均重新执行并 PASS;未使用 Jest。
|
||
- 原 NOT_PROVEN 不因实现存在而猜成 PASS;85 条继续保持 NOT_PROVEN。一条旧 NOT_PROVEN 因精确 witness 得到反证,改为 FAIL。
|
||
- OpenCPN 校验清单 25/25 PASS;固定源码未替换、未联网补齐缺失资源。
|
||
|
||
## 3. Gate A–I
|
||
|
||
| Gate | 状态 | 结论 |
|
||
|---|---|---|
|
||
{gate_table}
|
||
|
||
## 4. 生产与证据边界
|
||
|
||
ordered Rule IR 已进入 v5 builder contract,release summary 的 plan/release identity、jobs、PBF 和 feature 数一致。Gate F 仍为 NOT_PROVEN,因为交付 PBF 不能唯一恢复全部原始 presence/value、关联对象与视图状态;Gate G 仍受固定快照缺少独立 chartsymbols.xml 限制;Gate I 的跨对象全局 pass interleaving 没有被局部 executor witness 证明。
|
||
|
||
## 5. 206 条 branch 复判
|
||
|
||
{branch_rows(branches)}
|
||
## 6. 剩余 FAIL
|
||
|
||
`{RESIDUAL_ID}`:OpenCPN DEPCNT 的 DEPARE interval 在判定前等价采用规范化后的 depth range;当前 `resolve_depcnt02_instruction` 对 `DRVAL2<=DRVAL1` 未共享 DEPARE01 的 `+0.01` 规范化,因此把 witness 错判为 safe contour。证据包:`{RESIDUAL_JSON.relative_to(ROOT)}`。
|
||
"""
|
||
V3_REPORT.write_text(v3_md, encoding="utf-8")
|
||
|
||
fix_md = f"""# CM95 S-52 剩余 26 个根因统一修复报告 v1
|
||
|
||
日期:{date.today()}。
|
||
|
||
## 1. 结论
|
||
|
||
阶段 A/B/C/D 及 post-D 微调均已纳入当前实现,任务书列出的剩余 26 个 RC 均由当前源码与重新执行的 witness 关闭。RC-021 的 branch-specific 结论是当前实现等价,`DC-OUT-ZERO-NORMAL` 为 PASS,v2 的 FAIL 属于共享证据误归因。
|
||
|
||
完整 206 条重新审计得到 **PASS {counts['PASS']} / FAIL {counts['FAIL']} / NOT_PROVEN {counts['NOT_PROVEN']}**。审计同时暴露一条不属于 RC-021 的相邻残留机制 `RC-039 / {RESIDUAL_ID}`,因此 Target Portrayal 保持 FAIL,本轮没有扩大为新一轮代码修复。
|
||
|
||
## 2. Production / release
|
||
|
||
- plan:`{PLAN.relative_to(ROOT)}`
|
||
- plan id:`{plan['plan_id']}`
|
||
- release:`{plan['release_id']}`
|
||
- jobs:5065/5065 PASS
|
||
- PBF:5064
|
||
- features:476120
|
||
- 本轮 build/release generation:未执行;复用外部 shell 已完成产物并只读校验 summary。
|
||
- PBF contract:已由阶段 A 引入 ordered Rule IR,v5 plan 使用 `cm95pbf-builder/0.4.13-s52-ordered-rule-ir`。
|
||
- 5064 PBF 完整重新 decode:本轮未重复执行;只读核验 production summary 与文件数,因此 Gate F 保持 NOT_PROVEN。
|
||
|
||
## 3. 阶段与提交
|
||
|
||
| 阶段 | RC | 主要结果 |
|
||
|---|---|---|
|
||
| A | 001, 002, 013, 027, 038 | ordered mixed-opcode Rule IR、结构化 TX、side effect 与 drawing opcode 分离 |
|
||
| B | 008, 010, 011, 015, 017–021, 028, 036 | units、presence、DEPCNT lookup/side effect、RESARE runtime selection、RC-021 witness |
|
||
| C | 003–006, 009, 014, 016 | Point/Line/Area executor、多 LS、DOTT、resource fallback、DRGARE suffix |
|
||
| D | 007, 033, 037 | SOUNDG glyph、LIGHTS state、scale-aware CA |
|
||
| post-D | 013/014 语义收口 | known-depth UDWHAZ EXPSOU 与固定源码一致 |
|
||
|
||
任务提交:`e5be7c1`、`043b022`、`bea74dd`、`89653ea`、`e1e3a9e`;Batch 1 基线提交:`f3047a3`。
|
||
|
||
- 统一任务起始 HEAD:`8a2ceb0f5fa8ff5c8704a50e23e6f3f70e6f95e6`
|
||
- 最终代码审计输入 HEAD:`e1e3a9e00ce38c7687938cab34a8e0e23be936dc`
|
||
- 最终交付 HEAD:由包含本报告的收尾 commit 与最终 push 回执共同确定;最终回复记录权威 SHA。
|
||
|
||
### 3.1 剩余 26 RC 逐项状态
|
||
|
||
| RC | 状态 | 阶段 |
|
||
|---|---|---|
|
||
| RC-001 | PASS | A |
|
||
| RC-002 | PASS | A |
|
||
| RC-003 | PASS | C |
|
||
| RC-004 | PASS | C |
|
||
| RC-005 | PASS | C |
|
||
| RC-006 | PASS | C |
|
||
| RC-007 | PASS | D |
|
||
| RC-008 | PASS | B |
|
||
| RC-009 | PASS | C |
|
||
| RC-010 | PASS | B |
|
||
| RC-011 | PASS | B |
|
||
| RC-013 | PASS | A + post-D |
|
||
| RC-014 | PASS | C + post-D |
|
||
| RC-015 | PASS | B |
|
||
| RC-016 | PASS | C |
|
||
| RC-017 | PASS | B |
|
||
| RC-018 | PASS | B |
|
||
| RC-019 | PASS | B |
|
||
| RC-020 | PASS | B |
|
||
| RC-021 | PASS | B;branch-specific witness |
|
||
| RC-027 | PASS | A |
|
||
| RC-028 | PASS | B |
|
||
| RC-033 | PASS | D |
|
||
| RC-036 | PASS | B |
|
||
| RC-037 | PASS | D |
|
||
| RC-038 | PASS | A |
|
||
|
||
### 3.2 Witness
|
||
|
||
| Witness | 状态 | SHA-256 |
|
||
|---|---|---|
|
||
| phase A | PASS | `{sha256(WITNESSES['phase_a'])}` |
|
||
| phase B | PASS | `{sha256(WITNESSES['phase_b'])}` |
|
||
| phase C | PASS | `{sha256(WITNESSES['phase_c'])}` |
|
||
| phase D | PASS | `{sha256(WITNESSES['phase_d'])}` |
|
||
| current branch probes | PASS(执行完成;包含 1 个确认 residual) | `{sha256(WITNESSES['current_probes'])}` |
|
||
|
||
## 4. Gate A–I
|
||
|
||
| Gate | 状态 | 结论 |
|
||
|---|---|---|
|
||
{gate_table}
|
||
|
||
## 5. 残留项
|
||
|
||
唯一 branch FAIL:`{RESIDUAL_ID}`。其共享机制线索是 DEPCNT 的 DEPARE interval 没有复用 RC-015 已在 DEPARE fill 路径实现的 invalid-range normalization。紧凑证据包:`{RESIDUAL_JSON.relative_to(ROOT)}`。
|
||
|
||
流程级 Gate A 仍有 `$AREAS/$SCODE` 与 lookup parser 语义差异;它们不是本轮新修代码范围。Gate F/G/I 继续保守为 NOT_PROVEN。
|
||
|
||
## 6. 交付物
|
||
|
||
- `{V3_REPORT.relative_to(ROOT)}`
|
||
- `{V3_JSON.relative_to(ROOT)}`
|
||
- `{FIX_JSON.relative_to(ROOT)}`
|
||
- `{RESIDUAL_JSON.relative_to(ROOT)}`
|
||
|
||
本任务代码修改范围:`src/cm95pbf/{{csp,presentation,s52,mapping}}.py`、`tools/cm95_s52_style_compiler.py`、`web/{{kyushu_compare.html,kyushu_compare_server.py,s52_runtime_executor.js}}`、runtime capability registry。收尾新增文件为本节四项产物及 `tools/cm95_s52_code_first_reaudit_v3.py`。既有 importer/Linux/README 等无关 dirty/untracked 文件均按 SHA-256 保护,未纳入提交。
|
||
"""
|
||
FIX_REPORT.write_text(fix_md, encoding="utf-8")
|
||
|
||
print(json.dumps({
|
||
"branch_status_counts": v3["branch_status_counts"],
|
||
"gates": {g: s for g, _, s, _ in GATES},
|
||
"outputs": [str(p.relative_to(ROOT)) for p in (V3_JSON, V3_REPORT, FIX_JSON, FIX_REPORT, RESIDUAL_JSON)],
|
||
}, ensure_ascii=False))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|