Index AXIS parity release evidence

This commit is contained in:
2026-08-23 10:21:35 +08:00
parent 00d97fc2d4
commit 4a11d9692b
5 changed files with 227 additions and 1 deletions

View File

@@ -0,0 +1,110 @@
{
"boundaries": [
"index does not promote partial reports",
"Chromium authorization is scoped to DOM/visual/performance evidence",
"release gate remains STOP_RELEASE until all blockers are separately closed"
],
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"implementation_status": "evidence_index_complete_release_blocked",
"integrity": {
"all_report_files_present": true,
"all_report_hashes_present": true,
"release_gate_preserved": true
},
"ownership": {
"physical_safety": "REAL-EXCLUDED",
"runtime_authority": "Dedicated Worker/WASM",
"source_oracle": "locked LinuxCNC 2.10.0~pre0",
"visual_authority": "Chromium authorized slice only"
},
"release_decision": {
"candidate_eligible": false,
"global_blocked_count": 6,
"reasons": [
"Firefox/Safari minimum browser matrix remains TBD/unavailable",
"OPFS worker timeout and crash/restart Playwright evidence unavailable",
"lathe/gantry/VisMach-5-axis exact resource closure remains blocked",
"official kinematics oracle remains separately blocked"
],
"release_action": "STOP_RELEASE",
"status": "blocked"
},
"report_sha256": "1a0d3add58fcddf8a6a2a81a7197d91c45a1c94fabb7db75033c4b2f0f3da777",
"reports": [
{
"file_sha256": "6017dde3a35234ba597dd3ca9bc0551cc380d101d7ab197fb3bdb481f1610542",
"implementation_status": "source_frozen_and_inventory",
"path": "coverage/axis-source-inventory.report.json",
"report_sha256": "0ec92a467ca13700bd12a7f2889ada8aa11588ca50858dbe25f84a5c946b8176",
"schema": "axis-source-inventory.v1",
"status": "verified",
"task_id": "AXIS-INV-01"
},
{
"file_sha256": "4f3a71108441caa7255d2097650d0b14ae421ef07290d4b544b37d4d9d919486",
"implementation_status": "pure_worker_gate_contract",
"path": "coverage/axis-contract.report.json",
"report_sha256": "5de8d083c7903cf6e4753ff1adcbc26b8a868ef1f911ecbc19f56665e5805a44",
"schema": "axis-contract-report.v1",
"status": "verified",
"task_id": "AXIS-CON-01..04"
},
{
"file_sha256": "90baa63d72ccdef3d55b9611e130a041ac813fc744d1311465e5dd5a18ddd7d9",
"implementation_status": "worker_backed_operation_controller",
"path": "coverage/axis-control.report.json",
"report_sha256": "cc12b349cc3fc3e7d1882530f123ca92e4a510f3f40fec4c72bcfd4e876f0a3e",
"schema": "axis-control-report.v1",
"status": "verified",
"task_id": "AXIS-CTRL-01..08"
},
{
"file_sha256": "452a1b13aa4fe75f9fe4cfe54679360756acea7dac87e4abdcb5118fe7ed6bab",
"implementation_status": "authoritative_trajectory_dro_editor",
"path": "coverage/axis-view-edit.report.json",
"report_sha256": "39785b377806de18cb48a1d3c38c129d84a000b9c81d2478cc1a5567ec754e9b",
"schema": "axis-view-edit-report.v1",
"status": "verified",
"task_id": "AXIS-VIEW-01..04+AXIS-EDIT-01"
},
{
"file_sha256": "3b72163e5ed029b30f4637b3794d404d1f7bf55cdb7a3f6ee0e278b1ddc52211",
"implementation_status": "representative_cases_verified_remaining_families_blocked",
"path": "coverage/axis-case-parity.report.json",
"report_sha256": "a6fcc5eec964a02b821f9a52f30b8e0d3303b8c94507b6d44d6cc75b7a7089e4",
"schema": "axis-case-parity-report.v1",
"status": "partial",
"task_id": "AXIS-CASE-01..04"
},
{
"file_sha256": "aa7fcb014780e8177b379fc4e660890054456ade5bf9243496a6d2e97e1a4365",
"implementation_status": "evidence_aggregated",
"path": "coverage/axis-test-matrix.report.json",
"report_sha256": "9ba5de25bd8cfbfd61bd0a87bcb282170fa1de44b28a172b1c6ba649f580c357",
"schema": "axis-test-matrix-report.v1",
"status": "partial",
"task_id": "AXIS-TEST-01..06"
},
{
"file_sha256": "47cca469a22986235ea147333f8b9b6902480793db5c554a6267840c0b572f95",
"implementation_status": "verified",
"path": "coverage/gui-browser-visual-fe-m4.report.json",
"report_sha256": "301fae229574e9e0bb4090330ed6d783da4e9771a1c1666473b1cf011f6330d6",
"schema": "gui-browser-visual-fe-m4-report.v1",
"status": "verified",
"task_id": "FE-M4-GUI-06"
}
],
"schema": "axis-release-evidence-report.v1",
"status": "partial",
"summary": {
"blocked": 0,
"blocked_case_count": 6,
"chromium_authorized": true,
"partial": 2,
"total_reports": 7,
"verified": 5,
"verified_case_count": 5
},
"task_id": "AXIS-REL-01/02"
}

View File

@@ -558,7 +558,7 @@ M0 的 `AXIS-INV-01…04` 已执行:[`coverage/axis-source-inventory.report.js
5. `AXIS-VIEW-01…04` 和 `AXIS-EDIT-01` 已完成:[`coverage/axis-view-edit.report.json`](../coverage/axis-view-edit.report.json) 固化 Worker authoritative planner trajectory、6 camera modes、8 preview layers、DRO coordinate/display/unit modes、current-line/aria-current/diagnostic editor evidence;报告哈希 `39785b377806de18cb48a1d3c38c129d84a000b9c81d2478cc1a5567ec754e9b`。
6. `AXIS-CASE-01…04` 已形成案例矩阵:[`coverage/axis-case-parity.report.json`](../coverage/axis-case-parity.report.json) 中 Minimal XYZ、CoreXY kins/HAL、Linear/Rotary Delta representative verified;lathe/gantry/VisMach-5-axis 仍按 exact resource/startup 与 official kinematics oracle 保持 blocked。报告哈希 `a6fcc5eec964a02b821f9a52f30b8e0d3303b8c94507b6d44d6cc75b7a7089e4`。
7. `AXIS-TEST-01…06` 已形成测试矩阵:[`coverage/axis-test-matrix.report.json`](../coverage/axis-test-matrix.report.json) 中 DOM/accessibility、Chromium visual、operation replay、Worker case parity 和 Chromium repeat performance 已验证;Firefox/Safari minimum matrix、OPFS recovery 和 6 个案例资源/oracle blocker 仍明确 blocked。报告哈希 `9ba5de25bd8cfbfd61bd0a87bcb282170fa1de44b28a172b1c6ba649f580c357`。
8. `AXIS-REL-01/02`:只有所有 P0/P1 和报告自哈希通过,才更新 release gate。
8. `AXIS-REL-01/02` 已完成 AXIS 专用 evidence index:[`coverage/axis-release-evidence.report.json`](../coverage/axis-release-evidence.report.json) 索引 7 份报告、file/report hash、owner、verified/partial 状态和 blocker 分类;release gate 保持 `STOP_RELEASE`,报告哈希 `1a0d3add58fcddf8a6a2a81a7197d91c45a1c94fabb7db75033c4b2f0f3da777`。
第一批可执行命令:

View File

@@ -49,6 +49,7 @@ direct_inputs:
- coverage/axis-view-edit.report.json
- coverage/axis-case-parity.report.json
- coverage/axis-test-matrix.report.json
- coverage/axis-release-evidence.report.json
- coverage/gui-browser-visual-fe-m4.report.json
- docs/linuxcnc-axis-complete-parity-spec.md
- coverage/browser-matrix-remediation.report.json
@@ -86,6 +87,8 @@ non_goals:
axis_case_parity_report_sha256: a6fcc5eec964a02b821f9a52f30b8e0d3303b8c94507b6d44d6cc75b7a7089e4
axis_test_matrix: partial
axis_test_matrix_report_sha256: 9ba5de25bd8cfbfd61bd0a87bcb282170fa1de44b28a172b1c6ba649f580c357
axis_release_evidence: partial
axis_release_evidence_report_sha256: 1a0d3add58fcddf8a6a2a81a7197d91c45a1c94fabb7db75033c4b2f0f3da777
browser_matrix: blocked
opfs_recovery: blocked
prior_completion_evidence:

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Generate the auditable AXIS parity evidence index and release decision."""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
REPORT = ROOT / "coverage/axis-release-evidence.report.json"
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def load(path: str) -> dict[str, Any]:
return json.loads((ROOT / path).read_text(encoding="utf-8"))
def sha(path: str) -> str:
return hashlib.sha256((ROOT / path).read_bytes()).hexdigest()
def entry(path: str) -> dict[str, Any]:
value = load(path)
return {"path": path, "schema": value.get("schema"), "task_id": value.get("task_id"), "status": value.get("status"), "implementation_status": value.get("implementation_status"), "report_sha256": value.get("report_sha256"), "file_sha256": sha(path)}
def generate() -> dict[str, Any]:
reports = [
"coverage/axis-source-inventory.report.json",
"coverage/axis-contract.report.json",
"coverage/axis-control.report.json",
"coverage/axis-view-edit.report.json",
"coverage/axis-case-parity.report.json",
"coverage/axis-test-matrix.report.json",
"coverage/gui-browser-visual-fe-m4.report.json",
]
gate = load("coverage/release-candidate-gate.report.json")
entries = [entry(path) for path in reports]
counts = {status: sum(row["status"] == status for row in entries) for status in ("verified", "partial", "blocked")}
report: dict[str, Any] = {
"schema": "axis-release-evidence-report.v1",
"task_id": "AXIS-REL-01/02",
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"status": "partial",
"implementation_status": "evidence_index_complete_release_blocked",
"reports": entries,
"summary": {"total_reports": len(entries), "verified": counts["verified"], "partial": counts["partial"], "blocked": counts["blocked"], "chromium_authorized": True, "verified_case_count": 5, "blocked_case_count": 6},
"release_decision": {
"status": "blocked",
"release_action": gate.get("release_action", "STOP_RELEASE"),
"candidate_eligible": gate.get("candidate_eligible", False),
"global_blocked_count": gate.get("blocked_count"),
"reasons": [
"Firefox/Safari minimum browser matrix remains TBD/unavailable",
"OPFS worker timeout and crash/restart Playwright evidence unavailable",
"lathe/gantry/VisMach-5-axis exact resource closure remains blocked",
"official kinematics oracle remains separately blocked",
],
},
"ownership": {"source_oracle": "locked LinuxCNC 2.10.0~pre0", "runtime_authority": "Dedicated Worker/WASM", "visual_authority": "Chromium authorized slice only", "physical_safety": "REAL-EXCLUDED"},
"integrity": {"all_report_files_present": all((ROOT / path).is_file() for path in reports), "all_report_hashes_present": all(row["report_sha256"] for row in entries), "release_gate_preserved": gate.get("release_action") == "STOP_RELEASE" and gate.get("candidate_eligible") is False},
"boundaries": ["index does not promote partial reports", "Chromium authorization is scoped to DOM/visual/performance evidence", "release gate remains STOP_RELEASE until all blockers are separately closed"],
}
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
REPORT.parent.mkdir(parents=True, exist_ok=True)
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
if __name__ == "__main__":
result = generate()
print(f"axis_release_evidence={result['status']} reports={result['summary']['total_reports']} release={result['release_decision']['release_action']} report_sha256={result['report_sha256']}")

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Contract tests for AXIS-REL-01/02 evidence."""
from __future__ import annotations
import hashlib
import json
import unittest
from pathlib import Path
import generate_axis_release_evidence
ROOT = Path(__file__).resolve().parents[1]
REPORT = ROOT / "coverage/axis-release-evidence.report.json"
class AxisReleaseEvidenceTests(unittest.TestCase):
def test_contract(self) -> None:
report = json.loads(REPORT.read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "axis-release-evidence-report.v1")
self.assertEqual(report["task_id"], "AXIS-REL-01/02")
self.assertEqual(report["status"], "partial")
self.assertEqual(report["summary"]["total_reports"], 7)
self.assertGreaterEqual(report["summary"]["verified"], 5)
self.assertEqual(report["release_decision"]["release_action"], "STOP_RELEASE")
self.assertFalse(report["release_decision"]["candidate_eligible"])
self.assertTrue(report["integrity"]["release_gate_preserved"])
def test_self_hash(self) -> None:
report = json.loads(REPORT.read_text(encoding="utf-8"))
unsigned = {key: value for key, value in report.items() if key != "report_sha256"}
self.assertEqual(report["report_sha256"], hashlib.sha256(generate_axis_release_evidence.canonical(unsigned).encode()).hexdigest())
if __name__ == "__main__":
unittest.main()