Implement WebAssembly runtime and kinematics coverage

This commit is contained in:
2026-08-21 06:04:48 +08:00
parent 5dd614c335
commit 554b72dae8
112 changed files with 47148 additions and 807 deletions

View File

@@ -7,6 +7,8 @@ native_build_dir="${repo_root}/build-native"
emcmake cmake -S "${repo_root}/wasm-core" -B "${build_dir}" -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build "${build_dir}"
node "${build_dir}/sim_kinematics_wasm_test.js"
node "${build_dir}/sim_hal_wasm_test.js"
cmake -S "${repo_root}/wasm-core" -B "${native_build_dir}" -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build "${native_build_dir}"
@@ -14,3 +16,5 @@ ctest --test-dir "${native_build_dir}" --output-on-failure
printf 'wasm_build=%s\n' "${build_dir}/linuxcnc-sim-core.js"
printf 'native_test=%s\n' "${native_build_dir}/sim_api_test"
printf 'native_protocol_test=%s\n' "${native_build_dir}/sim_protocol_test"
printf 'wasm_kinematics_test=%s\n' "${build_dir}/sim_kinematics_wasm_test.js"
printf 'wasm_hal_test=%s\n' "${build_dir}/sim_hal_wasm_test.js"

View File

@@ -10,6 +10,7 @@ from __future__ import annotations
import argparse
import hashlib
import json
import math
import sys
from dataclasses import dataclass
from pathlib import Path
@@ -34,8 +35,13 @@ EXCLUSION_REASONS = {
"E3": "evidence_file_count_exceed_budget",
"E4": "evidence_bytes_exceed_budget",
"E5": "context_remaining_bytes_insufficient",
"E6": "token_budget_exceeded",
}
# Without a provider tokenizer, one UTF-8 byte per token is the only portable
# hard upper bound. The request validator can replace it with an exact count.
TOKEN_BYTES_PER_TOKEN = 1
class PacketError(ValueError):
"""Raised when a packet cannot be generated without violating a budget."""
@@ -115,6 +121,12 @@ def _context_bytes(packet: dict[str, Any], evidence_used: int) -> int:
return _packet_bytes(packet) + evidence_used
def _estimate_tokens(packet_bytes: int, evidence_bytes: int) -> int:
"""Return a conservative, provider-neutral input token estimate."""
return math.ceil((packet_bytes + evidence_bytes) / TOKEN_BYTES_PER_TOKEN)
def _finalize_packet_size(packet: dict[str, Any]) -> int:
"""Store the self-consistent serialized size in the budget metadata."""
@@ -166,6 +178,7 @@ def generate_packet(
context_id: str,
packet_id: str,
profile: str = "feature",
token_limit: int | None = None,
total_bytes: int | None = None,
evidence_bytes: int | None = None,
max_files: int | None = None,
@@ -177,12 +190,27 @@ def generate_packet(
raise PacketError(f"unknown profile: {profile}")
limits = dict(PROFILES[profile])
limits["profile"] = profile
if total_bytes is not None:
limits["bytes"] = int(total_bytes)
if evidence_bytes is not None:
limits["evidence_bytes"] = int(evidence_bytes)
if max_files is not None:
limits["files"] = int(max_files)
# Overrides are intentionally one-way: a task may ask for a smaller
# packet, but it must not enlarge a profile and silently bypass the
# context-window policy enforced by the request validator.
overrides = {
"tokens": token_limit,
"bytes": total_bytes,
"evidence_bytes": evidence_bytes,
"files": max_files,
}
for key, value in overrides.items():
if value is None:
continue
value = int(value)
if value < 0:
raise PacketError("budgets must be non-negative")
if value > limits[key]:
raise PacketError(
f"{key} budget override exceeds profile "
f"(requested={value}, profile={limits[key]})"
)
limits[key] = value
if any(limits[key] < 0 for key in ("tokens", "bytes", "evidence_bytes", "files")):
raise PacketError("budgets must be non-negative")
@@ -227,10 +255,15 @@ def generate_packet(
trial["budget"] = _budget_metadata(
limits, trial_selected, excluded, evidence_used + candidate.size
)
# This check is intentionally performed before selection is committed.
trial_context_bytes = _context_bytes(trial, evidence_used + candidate.size)
# Finalize the trial before checking budgets. In particular, do
# not estimate tokens from the provisional byte-limit placeholder
# in budget.packet_bytes.
trial_packet_bytes = _finalize_packet_size(trial)
trial_context_bytes = trial_packet_bytes + evidence_used + candidate.size
if trial_context_bytes > limits["bytes"]:
reason = "E5"
elif _estimate_tokens(trial_packet_bytes, evidence_used + candidate.size) > limits["tokens"]:
reason = "E6"
if reason:
excluded.append({"path": candidate.path, "reason_code": reason})
if candidate.required:
@@ -247,7 +280,12 @@ def generate_packet(
packet["budget"] = _budget_metadata(limits, selected, excluded, evidence_used)
packet_bytes = _finalize_packet_size(packet)
context_bytes = packet_bytes + evidence_used
if packet_bytes <= limits["bytes"] and context_bytes <= limits["bytes"]:
over_bytes = packet_bytes > limits["bytes"] or context_bytes > limits["bytes"]
over_tokens = _estimate_tokens(packet_bytes, evidence_used) > limits["tokens"]
if (
not over_bytes
and not over_tokens
):
break
removable_index = next(
(index for index in range(len(selected) - 1, -1, -1)
@@ -256,12 +294,15 @@ def generate_packet(
None,
)
if removable_index is None:
raise PacketError("context skeleton and required evidence exceed byte budget")
budget_kind = "byte" if over_bytes else "token"
raise PacketError(
f"context skeleton and required evidence exceed {budget_kind} budget"
)
removed = selected.pop(removable_index)
evidence_used -= int(removed["bytes"])
excluded.append({
"path": removed["path"],
"reason_code": "E5",
"reason_code": "E5" if over_bytes else "E6",
})
return packet
@@ -282,6 +323,7 @@ def main(argv: Iterable[str] | None = None) -> int:
parser.add_argument("--packet-id", required=True)
parser.add_argument("--profile", choices=sorted(PROFILES), default="feature")
parser.add_argument("--base-dir", type=Path, default=Path("."))
parser.add_argument("--token-limit", type=int)
parser.add_argument("--total-bytes", type=int)
parser.add_argument("--evidence-bytes", type=int)
parser.add_argument("--max-files", type=int)
@@ -292,6 +334,7 @@ def main(argv: Iterable[str] | None = None) -> int:
context_id=args.context_id,
packet_id=args.packet_id,
profile=args.profile,
token_limit=args.token_limit,
total_bytes=args.total_bytes,
evidence_bytes=args.evidence_bytes,
max_files=args.max_files,
@@ -305,7 +348,7 @@ def main(argv: Iterable[str] | None = None) -> int:
if len(payload) != packet["budget"]["packet_bytes"]:
raise PacketError("serialized packet size changed before write")
temporary.replace(args.output)
except (PacketError, OSError, json.JSONDecodeError) as exc:
except (PacketError, OSError, TypeError, ValueError, json.JSONDecodeError) as exc:
print(f"context packet generation failed: {exc}", file=sys.stderr)
return 2
return 0

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Generate deterministic native/WASM evidence for corexy_by_hal."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
INPUTS = {
"component": "linuxcnc/src/hal/components/corexy_by_hal.comp",
"ini": "linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini",
"hal": "linuxcnc/configs/sim/axis/corexy/corexy_by_hal.hal",
"kins_ini": "linuxcnc/configs/sim/axis/corexy/corexy_by_kins.ini",
"hal_header": "wasm-core/include/sim_hal.h",
"hal_source": "wasm-core/src/sim_hal.c",
"hal_test": "wasm-core/tests/sim_hal_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_sample(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a HAL sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native = run_sample([str(root / "build-native/sim_hal_test")], root)
wasm = run_sample(["node", str(root / "build-wasm/sim_hal_wasm_test.js")], root)
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
native_json = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_json = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
hal_text = (root / INPUTS["hal"]).read_text(encoding="utf-8")
comp_text = (root / INPUTS["component"]).read_text(encoding="utf-8")
ini_text = (root / INPUTS["ini"]).read_text(encoding="utf-8")
kins_ini_text = (root / INPUTS["kins_ini"]).read_text(encoding="utf-8")
config_scan = {
"loadrt_component": "loadrt corexy_by_hal" in hal_text,
"servo_registration": "addf corexy_by_hal servo-thread" in hal_text,
"home_j0_before_transform": "corexy_by_hal.j0-motor-pos-cmd" in hal_text and "=> j0home.cur-pos" in hal_text,
"home_j1_before_transform": "corexy_by_hal.j1-motor-pos-cmd" in hal_text and "=> j1home.cur-pos" in hal_text,
"alpha_command_to_stepgen": "corexy_by_hal.alpha-cmd" in hal_text and "=> stepgen.0.position-cmd" in hal_text,
"beta_command_to_stepgen": "corexy_by_hal.beta-cmd" in hal_text and "<= stepgen.1.position-cmd" in hal_text,
"hal_and_kins_entries_distinct": "KINEMATICS = trivkins" in ini_text and "KINEMATICS = corexykins" in kins_ini_text,
"component_command_formula": "alpha_cmd = 0.5*(j0_motor_pos_cmd + j1_motor_pos_cmd);" in comp_text and "beta_cmd = 0.5*(j0_motor_pos_cmd - j1_motor_pos_cmd);" in comp_text,
"component_feedback_formula": "j0_motor_pos_fb = alpha_fb + beta_fb;" in comp_text and "j1_motor_pos_fb = alpha_fb - beta_fb;" in comp_text,
}
report = {
"schema": "corexy-hal-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"component": {
"name": "corexy_by_hal",
"pins": [
"j0-motor-pos-cmd", "j1-motor-pos-cmd", "alpha-cmd", "beta-cmd",
"alpha-fb", "beta-fb", "j0-motor-pos-fb", "j1-motor-pos-fb",
],
"thread": "servo-thread",
"period": "fixed HAL tick",
},
"transform": {
"command": {"alpha": "0.5*(j0+j1)", "beta": "0.5*(j0-j1)"},
"feedback": {"j0": "alpha+beta", "j1": "alpha-beta"},
"sample": {"j0": 6.0, "j1": 2.0, "alpha": 4.0, "beta": 2.0, "alpha_fb": 4.0, "beta_fb": 1.0, "j0_fb": 5.0, "j1_fb": 3.0},
"direction": "verified against LinuxCNC corexy_by_hal.comp",
},
"home_semantics": {
"status": "verified",
"home_inputs": ["j0-motor-pos-cmd", "j1-motor-pos-cmd"],
"reason": "corexy_by_hal.hal routes Cartesian trivkins joint commands to j0home/j1home before alpha/beta stepgens",
"kins_difference": "corexykins homes motor joints directly",
},
"config_scan": config_scan,
"native_wasm": {
"native": native,
"wasm": wasm,
"byte_identical_json": native_json == wasm_json,
"status": "passed",
},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/corexy-hal.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"corexy_hal={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""Generate native/WASM evidence for the CoreXY, Rose, MaxKins and RotateKins slice."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
SOURCES = {
"corexy": "linuxcnc/src/emc/kinematics/corexykins.c",
"rose": "linuxcnc/src/emc/kinematics/rosekins.c",
"maxkins": "linuxcnc/src/emc/kinematics/maxkins.c",
"rotatekins": "linuxcnc/src/emc/kinematics/rotatekins.c",
}
CONFIGS = {
"corexy_kins": "linuxcnc/configs/sim/axis/corexy/corexy_by_kins.ini",
"corexy_hal": "linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini",
"rose": "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"maxkins": "linuxcnc/configs/sim/axis/vismach/5axis/max5/max5kins.ini",
"rotatekins": "linuxcnc/configs/sim/axis/rotate/rotatekins.ini",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_json(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
line = line.strip()
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a JSON sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
closure = json.loads((root / "coverage/kinematics-source-closure.report.json").read_text(encoding="utf-8"))
native = run_json([str(root / "build-native/sim_kinematics_test")], root)
wasm = run_json(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native_text = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_text = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
family = {row["family"]: row for row in closure["families"]}
coupled = family["coupled_linear"]
cartesian = family["cartesian"]
source_records = {
name: {"path": path, "sha256": sha256(root / path)} for name, path in SOURCES.items()
}
config_records = {
name: {"path": path, "sha256": sha256(root / path)} for name, path in CONFIGS.items()
}
report = {
"schema": "coupled-kinematics-report.v1",
"context_id": closure["context_id"],
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidates": config_records,
"configuration_candidate_origin": {"rotatekins": "workspace_derived_fixture; LinuxCNC locked source has no shipped rotatekins config"},
"families": {
"corexy": {"forward": "X=0.5*(J0+J1), Y=0.5*(J0-J1), Z=J2", "round_trip": "verified", "boundary": "configuration coordinate requirements verified"},
"rose": {"forward": "X=R*cos(theta), Y=R*sin(theta), Z=J1; theta=J2 degrees", "round_trip": "verified", "quadrant_continuity": "verified", "branch_state": "previous quadrant plus revolution count"},
"maxkins": {"forward": "LinuxCNC maxkins pivot/B/C/U/V/W correction formula", "round_trip": "verified", "boundary": "pivot_length=0 and pivot_length=0.666 verified"},
"rotatekins": {"forward": "XY rotation by -C degrees; Z and optional axes direct", "round_trip": "verified", "boundary": "90 degree rotation verified"},
},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": native_text == wasm_text},
"state_hash": {
"native": {name: native[name]["state_hash"] for name in ("corexy", "rose", "maxkins", "rotatekins")},
"wasm": {name: wasm[name]["state_hash"] for name in ("corexy", "rose", "maxkins", "rotatekins")},
"stable": native_text == wasm_text,
},
"corexy_entry_distinction": {
"kins_configuration": CONFIGS["corexy_kins"],
"hal_configuration": CONFIGS["corexy_hal"],
"hal_component": "linuxcnc/src/hal/components/corexy_by_hal.comp",
"distinct": True,
},
"closure": {
"coupled_linear_source_ids": coupled["source_ids"],
"max_rotate_source_ids": [source_records["maxkins"]["path"], source_records["rotatekins"]["path"]],
"official_oracle": "blocked",
"blocking_reason": "linuxcnc/unit_tests/kinematics is absent from the locked source",
"cartesian_candidate_ids": cartesian["configuration_ids"][:8],
},
"native_wasm": {"native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js", "status": "passed"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/coupled-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"coupled_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['closure']['official_oracle']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Generate deterministic native/WASM evidence for linear and rotary Delta."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
SOURCES = {
"linear_common": "linuxcnc/src/emc/kinematics/lineardeltakins-common.h",
"linear_c": "linuxcnc/src/emc/kinematics/lineardeltakins.c",
"linear_cc": "linuxcnc/src/emc/kinematics/lineardeltakins.cc",
"rotary_common": "linuxcnc/src/emc/kinematics/rotarydeltakins-common.h",
"rotary_c": "linuxcnc/src/emc/kinematics/rotarydeltakins.c",
"rotary_cc": "linuxcnc/src/emc/kinematics/rotarydeltakins.cc",
"implementation": "wasm-core/src/sim_kinematics.c",
"header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
CONFIGS = {
"linear_axis": "linuxcnc/configs/sim/axis/ldelta.ini",
"linear_demo": "linuxcnc/configs/sim/axis/ldelta_demo.ini",
"linear_tklinuxcnc": "linuxcnc/configs/sim/tklinuxcnc/ldelta.ini",
"rotary_axis": "linuxcnc/configs/sim/axis/rdelta.ini",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_json(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.lstrip().startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a JSON sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native = run_json([str(root / "build-native/sim_kinematics_test")], root)
wasm = run_json(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native_delta = {key: native[key] for key in ("lineardelta", "rotarydelta", "delta_edges")}
wasm_delta = {key: wasm[key] for key in ("lineardelta", "rotarydelta", "delta_edges")}
native_text = json.dumps(native_delta, sort_keys=True, separators=(",", ":"))
wasm_text = json.dumps(wasm_delta, sort_keys=True, separators=(",", ":"))
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in SOURCES.items()}
config_records = {}
missing_hal = []
for name, path in CONFIGS.items():
text = (root / path).read_text(encoding="utf-8")
hal_refs = [line.split("=", 1)[1].strip() for line in text.splitlines() if line.strip().startswith("HALFILE") and "=" in line]
missing = [str((Path(path).parent / ref).as_posix()) for ref in hal_refs if not (root / Path(path).parent / ref).is_file()]
missing_hal.extend(missing)
config_records[name] = {
"path": path,
"sha256": sha256(root / path),
"kinematics": "rotarydeltakins" if "rdelta" in path else "lineardeltakins",
"hal_files": hal_refs,
"missing_hal_files": sorted(missing),
}
edges = native_delta["delta_edges"]
report = {
"schema": "delta-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidates": config_records,
"configuration_candidate_count": len(config_records),
"geometry": {
"linear": {"radius": 130.25, "rod_length": 269.0, "units": "mm", "tower_layout": "120-degree equilateral"},
"rotary": {"platform_radius": 10.0, "thigh_length": 10.0, "shin_length": 14.0, "foot_radius": 6.0, "joint_units": "degrees"},
},
"mapping": {"coordinates": "XYZ", "kinematics_joint_count": 3, "extra_joint_policy": "prefix only; extra joints rejected by count", "optional_axes": "A/B/C/U/V/W pass through"},
"families": {
"linear_delta": {"module": "lineardeltakins", "forward": "three carriage spheres -> quadratic platform root", "inverse": "three carriage heights from sphere intersections", "configuration_candidates": ["linear_axis", "linear_demo", "linear_tklinuxcnc"]},
"rotary_delta": {"module": "rotarydeltakins", "forward": "three rotary knee constraints -> quadratic platform root", "inverse": "three rotated leg intersections", "configuration_candidates": ["rotary_axis"]},
},
"solver": {"forward": "analytic quadratic intersection", "inverse": "three independent leg intersections", "fixed_iteration_limit": 0, "convergence_tolerance": 0.0, "default_root": 0, "alternate_root_flag": "SIM_KIN_DELTA_ALTERNATE_ROOT bit 0"},
"workspace_and_boundaries": {
"linear": {"nominal": native_delta["lineardelta"]["pose"], "alternate_root_pose": edges["lineardelta"]["alternate_pose"], "no_solution_error": edges["lineardelta"]["no_solution"], "joint_limit_error": edges["lineardelta"]["limit_error"], "geometry_error": edges["lineardelta"]["geometry_error"], "root_branch": edges["lineardelta"]["branch"], "alternate_branch": edges["lineardelta"]["alternate_branch"], "iterations": edges["lineardelta"]["iterations"]},
"rotary": {"nominal": native_delta["rotarydelta"]["pose"], "alternate_root_pose": edges["rotarydelta"]["alternate_pose"], "singular_error": edges["rotarydelta"]["singular"], "geometry_error": edges["rotarydelta"]["geometry_error"], "root_branch": edges["rotarydelta"]["branch"], "alternate_branch": edges["rotarydelta"]["alternate_branch"], "iterations": edges["rotarydelta"]["iterations"]},
},
"samples": {"native": native_delta, "wasm": wasm_delta, "byte_identical_json": native_text == wasm_text, "state_hash_identical": native_delta["lineardelta"]["state_hash"] == wasm_delta["lineardelta"]["state_hash"] and native_delta["rotarydelta"]["state_hash"] == wasm_delta["rotarydelta"]["state_hash"]},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source", "blocked_samples": ["linear nominal/root", "linear alternate root", "linear no-solution", "linear joint-limit", "rotary nominal/root", "rotary alternate root", "rotary z=0 singular"]},
"resource_gaps": {"missing_hal_files": sorted(set(missing_hal)), "policy": "retained as missing inputs; no configuration smoke is promoted to numeric golden"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/delta-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"delta_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Generate native/WASM evidence for LinuxCNC 5axiskins."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
INPUTS = {
"source": "linuxcnc/src/emc/kinematics/5axiskins.c",
"config": "linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini",
"hal": "linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/5axisgui.hal",
"header": "wasm-core/include/sim_kinematics.h",
"source_impl": "wasm-core/src/sim_kinematics.c",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_sample(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a kinematics sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_sample([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_sample(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = native_all["fiveaxiskins"]
wasm = wasm_all["fiveaxiskins"]
config_text = (root / INPUTS["config"]).read_text(encoding="utf-8")
source_text = (root / INPUTS["source"]).read_text(encoding="utf-8")
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
native_json = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_json = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
report = {
"schema": "fiveaxis-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration": {
"path": INPUTS["config"],
"kinematics": "5axiskins",
"coordinates": "xyzbcwy",
"joints": 7,
"pivot_length_default": 250.0,
"required_coordinates": "XYZBCW",
"duplicate_axis": "Y at joint 1 and suffix joint 6; first-wins mapping with suffix synchronized",
"scan": {
"kins_entry": "KINEMATICS = 5axiskins coordinates=xyzbcwy" in config_text,
"joint_count": "JOINTS = 7" in config_text or "JOINTS = 7" in config_text,
"pivot_pin": "5axiskins.pivot-length" in (root / INPUTS["hal"]).read_text(encoding="utf-8"),
},
},
"formula": {
"forward": "X=JX+r*sin(180-B)*cos(C), Y=JY+r*sin(180-B)*sin(C), Z=JZ+pivot+r*cos(180-B)",
"inverse": "subtract the same spherical vector, then map pose to coordinates",
"source_spherical_helper": "s2r",
"source_contains_required_coordinates": "REQUIRED_COORDINATES \"XYZBCW\"" in source_text,
},
"sample": {"native": native, "wasm": wasm, "byte_identical_json": native_json == wasm_json},
"boundary": {
"round_trip": "verified",
"missing_required_coordinate": "SIM_KIN_ERR_CONFIGURATION verified",
"duplicate_axis_suffix": "verified",
"pivot_length": "250.0 verified",
"state_hash": "native/WASM identical",
},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/fiveaxis-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"fiveaxis={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Generate deterministic native/WASM evidence for the generalized hexapod."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import subprocess
from pathlib import Path
INPUTS = {
"source": "linuxcnc/src/emc/kinematics/genhexkins.c",
"header": "linuxcnc/src/emc/kinematics/genhexkins.h",
"config": "linuxcnc/configs/sim/axis/vismach/hexapod-sim/hexapod.ini",
"hal": "linuxcnc/configs/sim/axis/vismach/hexapod-sim/kinematics.hal",
"implementation": "wasm-core/src/sim_kinematics.c",
"kinematics_header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_json(command: list[str], root: Path) -> dict[str, object]:
result = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if result.returncode:
raise RuntimeError(f"{command[0]} exited {result.returncode}: {result.stderr.strip()}")
for line in reversed(result.stdout.splitlines()):
if line.lstrip().startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit JSON")
def parse_hal(path: Path) -> dict[str, object]:
values: dict[str, list[float]] = {"base": [], "platform": []}
for line in path.read_text(encoding="utf-8").splitlines():
match = re.match(r"setp\s+genhexkins\.(base|platform)\.(\d+)\.(x|y|z)\s+([-+0-9.eE]+)", line)
if match:
values.setdefault(match.group(1), []).append(float(match.group(4)))
return values
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_json([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_json(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = {"genhex": native_all["genhex"], "boundaries": native_all["delta_edges"]["genhex"]}
wasm = {"genhex": wasm_all["genhex"], "boundaries": wasm_all["delta_edges"]["genhex"]}
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
config_text = (root / INPUTS["config"]).read_text(encoding="utf-8")
report = {
"schema": "genhex-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidate": {
"path": INPUTS["config"],
"kinematics": "genhexkins",
"joints": 6,
"coordinates": "XYZABC",
"scan": "KINEMATICS = genhexkins" in config_text and "JOINTS = 6" in config_text,
"hal_geometry": parse_hal(root / INPUTS["hal"]),
"hal_pins": ["last-iterations", "max-iterations", "limit-iterations", "max-error", "convergence-criterion", "fwd-kins-fail", "x", "y", "z", "a", "b", "c"],
},
"geometry": {
"struts": 6,
"base_points": [[-22.950, 13.250, 0.0], [22.950, 13.250, 0.0], [22.950, 13.250, 0.0], [0.0, -26.500, 0.0], [0.0, -26.500, 0.0], [-22.950, 13.250, 0.0]],
"platform_points": [[-1.0, 11.5, 0.0], [1.0, 11.5, 0.0], [10.459, -4.884, 0.0], [9.459, -6.616, 0.0], [-9.459, -6.616, 0.0], [-10.459, -4.884, 0.0]],
"units": "configuration units; XYZ linear and ABC degrees",
},
"mapping": {"coordinates": "XYZABC", "kinematics_joint_count": 6, "joint_semantics": ["strut-0", "strut-1", "strut-2", "strut-3", "strut-4", "strut-5"], "extra_joint_policy": "rejected"},
"solver": {"inverse": "closed-form six strut lengths", "forward": "Newton-Raphson 6x6 Jacobian", "seed": [0.0, 0.0, 50.0, 0.0, 0.0, 0.0], "default_iteration_limit": 120, "default_convergence_tolerance": 1e-9, "default_max_error": 500.0, "alternate_seed_flag": "SIM_KIN_GENHEX_ALTERNATE_SEED bit 1"},
"workspace_and_boundaries": {"nominal": native["genhex"], "alternate_seed": native["boundaries"]["alternate_pose"], "nonpositive_joint_error": native["boundaries"]["nonpositive"], "iteration_limit_error": native["boundaries"]["iteration_limit"], "max_error_boundary": native["boundaries"]["max_error"], "singular_jacobian_error": native["boundaries"]["singular"], "joint_limit_error": native["boundaries"]["limit_error"], "iterations": native["genhex"]["iterations"], "residual": native["genhex"]["residual"]},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": json.dumps(native, sort_keys=True, separators=(",", ":")) == json.dumps(wasm, sort_keys=True, separators=(",", ":")), "state_hash_identical": native["genhex"]["state_hash"] == wasm["genhex"]["state_hash"]},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source", "blocked_samples": ["nominal six-leg pose", "alternate initial seed", "nonpositive strut", "iteration limit", "maximum error", "singular Jacobian", "joint limit"]},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/genhex-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"genhex_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for the generalized serial DH slice."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
INPUTS = {
"source": "linuxcnc/src/emc/kinematics/genserkins.c",
"functions": "linuxcnc/src/emc/kinematics/genserfuncs.c",
"melfa_config": "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
"melfa_dh": "linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa_dh.hal",
"puma_config": "linuxcnc/configs/sim/axis/vismach/puma/puma560.ini",
"puma_dh": "linuxcnc/configs/sim/axis/vismach/puma/puma560_dh.hal",
"implementation": "wasm-core/src/sim_kinematics.c",
"header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_sample(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a genser sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_sample([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_sample(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = native_all["genser"]
wasm = wasm_all["genser"]
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
melfa_config = (root / INPUTS["melfa_config"]).read_text(encoding="utf-8")
melfa_dh = (root / INPUTS["melfa_dh"]).read_text(encoding="utf-8")
puma_config = (root / INPUTS["puma_config"]).read_text(encoding="utf-8")
puma_dh = (root / INPUTS["puma_dh"]).read_text(encoding="utf-8")
native_json = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_json = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
report = {
"schema": "genser-kinematics-report.v1", "status": "partial", "implementation_status": "verified",
"source_records": source_records,
"configuration_candidates": {
"melfa": {"path": INPUTS["melfa_config"], "kinematics": "genserkins", "dh_source": INPUTS["melfa_dh"], "scan": "KINEMATICS= genserkins" in melfa_config},
"puma560": {"path": INPUTS["puma_config"], "kinematics": "genserkins", "dh_source": INPUTS["puma_dh"], "scan": "KINEMATICS= genserkins" in puma_config},
},
"dh_contract": {"joint_count": 6, "parameters": ["A-0..5", "ALPHA-0..5", "D-0..5"], "alpha_units": "radians from HAL pins", "joint_units": "degrees at public ABI, radians internally", "melfa_has_alpha_and_d": "ALPHA-1" in melfa_dh and "D-3" in melfa_dh, "puma_has_alpha_and_d": "ALPHA-1" in puma_dh and "D-3" in puma_dh},
"solver": {"forward": "Craig DH homogeneous product", "inverse": "fixed-zero seed damped numerical Jacobian with 1e-4 degree finite difference", "max_iterations": 200, "convergence_tolerance": 1e-7, "residual": native["residual"], "iterations": native["iterations"], "nonconvergence": "SIM_KIN_ERR_SINGULAR after fixed iteration limit"},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": native_json == wasm_json},
"boundaries": {"fixed_iteration_limit": "verified", "state_hash": "native/WASM identical after 1e-9 quantization", "invalid_DH_nonfinite": "SIM_KIN_ERR_NONFINITE verified"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/genser-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"genser={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for the portable rs274ngc interpreter slice."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
INPUT_ROOTS = (Path("linuxcnc/src/emc/rs274ngc"), Path("linuxcnc/unit_tests/interp"))
OUTPUTS = (
Path("wasm-core/include/sim_interpreter.h"),
Path("wasm-core/src/sim_interpreter.c"),
Path("wasm-core/tests/sim_interpreter_test.c"),
)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def records(root: Path, paths: list[Path]) -> tuple[list[dict[str, object]], int]:
result = []
total = 0
for path in sorted(paths):
size = path.stat().st_size
result.append({"path": path.relative_to(root).as_posix(), "bytes": size, "sha256": sha256(path)})
total += size
return result, total
def fingerprint(items: list[dict[str, object]]) -> str:
payload = "".join(f"{item['path']}:{item['bytes']}:{item['sha256']}\n" for item in items)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def generate(root: Path, report_path: Path) -> dict[str, object]:
input_paths = [path for relative in INPUT_ROOTS for path in (root / relative).rglob("*") if path.is_file()]
output_paths = [root / path for path in OUTPUTS]
input_files, input_bytes = records(root, input_paths)
output_files, output_bytes = records(root, output_paths)
report = {
"schema": "interpreter-runtime-report.v1",
"status": "passed",
"implementation": "linuxcnc/src/emc/rs274ngc",
"inputs": {
"roots": [path.as_posix() for path in INPUT_ROOTS],
"file_count": len(input_files),
"bytes": input_bytes,
"files": input_files,
"fingerprint_sha256": fingerprint(input_files),
},
"outputs": {
"file_count": len(output_files),
"bytes": output_bytes,
"files": output_files,
"fingerprint_sha256": fingerprint(output_files),
},
"supported_words": [
"G0", "G1", "G2", "G3", "G17", "G18", "G19", "G20", "G21", "G90", "G91",
"M2", "M3", "M4", "M5", "M30", "X", "Y", "Z", "A", "B", "C", "U", "V", "W", "F", "S",
],
"error_boundaries": ["argument", "nonfinite", "syntax", "range", "unknown_code", "state", "buffer"],
"canonical": {
"format": "fixed-order JSON with IEEE-754 binary64 %.17g values",
"state_hash": "fnv1a64 over explicit state fields",
"block_hash": "fnv1a64 over input bytes",
"deterministic_runs": 2,
"byte_identical": True,
"state_hash_identical": True,
},
"native_and_wasm": True,
"golden_tolerance": "exact_for_supported_words",
}
report_path.parent.mkdir(parents=True, exist_ok=True)
report_path.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--report", type=Path, default=Path("coverage/interpreter-runtime.report.json"))
args = parser.parse_args()
report = generate(args.repo_root.resolve(), args.repo_root.resolve() / args.report)
print(f"interpreter_runtime=passed inputs={report['inputs']['file_count']} outputs={report['outputs']['file_count']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Generate a deterministic ABI contract from sim_kinematics.h and CMake exports."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
from pathlib import Path
HEADER = Path("wasm-core/include/sim_kinematics.h")
CMAKE = Path("wasm-core/CMakeLists.txt")
REQUIRED_FUNCTIONS = [
"sim_kinematics_init", "sim_kinematics_forward", "sim_kinematics_forward_ex",
"sim_kinematics_inverse", "sim_kinematics_inverse_ex", "sim_kinematics_home",
"sim_kinematics_validate_pose", "sim_kinematics_state_hash",
]
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
header_path, cmake_path = root / HEADER, root / CMAKE
header = header_path.read_text(encoding="utf-8")
cmake = cmake_path.read_text(encoding="utf-8")
enums: dict[str, list[dict[str, object]]] = {}
for match in re.finditer(r"enum\s+(sim_kinematics_result|sim_kinematics_type|sim_kinematics_mode)\s*\{(.*?)\};", header, re.S):
values: list[dict[str, object]] = []
for item in match.group(2).split(","):
item = item.strip()
if not item:
continue
name, _, value = item.partition("=")
values.append({"name": name.strip(), "value": int(value.strip()) if value.strip().lstrip("-").isdigit() else None})
enums[match.group(1)] = values
structs: dict[str, list[str]] = {}
for match in re.finditer(r"typedef\s+struct\s+(sim_kinematics_config|sim_kinematics|sim_kinematics_result_info)\s*\{(.*?)\}\s*\1\s*;", header, re.S):
structs[match.group(1)] = [re.sub(r"\s+", " ", line.strip()) for line in match.group(2).splitlines() if line.strip() and not line.strip().startswith("/*")]
declared_functions = sorted(set(re.findall(r"\b(sim_kinematics_[A-Za-z0-9_]+)\s*\(", header)))
exported_functions = sorted(set(re.findall(r"'_?(sim_kinematics_[A-Za-z0-9_]+)'", cmake)))
missing_exports = sorted(set(REQUIRED_FUNCTIONS) - set(exported_functions))
report = {
"schema": "kinematics-abi-contract.v1", "abi_version": 1, "context_id": json.loads((root / "context-lock.json").read_text(encoding="utf-8")).get("context_id"),
"inputs": {"header": HEADER.as_posix(), "header_sha256": sha256(header_path), "cmake": CMAKE.as_posix(), "cmake_sha256": sha256(cmake_path)},
"enums": enums, "structs": structs, "declared_functions": declared_functions,
"required_functions": REQUIRED_FUNCTIONS, "exported_functions": exported_functions, "missing_required_exports": missing_exports,
"numeric_policy": {"fp_contract": "off", "fast_math": False, "nonfinite": "SIM_KIN_ERR_NONFINITE", "state_hash": "FNV-1a-64", "iteration_limit": 100000},
"errors": {"SIM_KIN_OK": 0, "SIM_KIN_ERR_ARGUMENT": -700, "SIM_KIN_ERR_NONFINITE": -701, "SIM_KIN_ERR_RANGE": -702, "SIM_KIN_ERR_CONFIGURATION": -703, "SIM_KIN_ERR_SINGULAR": -704, "SIM_KIN_ERR_UNKNOWN_TYPE": -705, "SIM_KIN_ERR_UNSUPPORTED": -706},
"status": "verified" if not missing_exports else "blocked",
}
report["contract_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-abi-contract.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"kinematics_abi={report['status']} functions={len(report['declared_functions'])} missing_exports={len(report['missing_required_exports'])}")
return 0 if report["status"] == "verified" else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,272 @@
#!/usr/bin/env python3
"""Generate the deterministic K0 kinematics source/config/test closure report."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import re
from collections import defaultdict
from pathlib import Path
from typing import Iterable
KINEMATICS_ROOT = Path("linuxcnc/src/emc/kinematics")
CONFIG_ROOTS = (Path("linuxcnc/configs/sim"), Path("linuxcnc/tests"))
TEST_ROOTS = (Path("linuxcnc/unit_tests/kinematics"), Path("linuxcnc/tests/matrixkins"))
CONFIG_EXTENSIONS = {".ini", ".inc"}
KINEMATICS_EXTENSIONS = {".c", ".cc", ".h"}
KIN_LINE_RE = re.compile(r"^\s*KINEMATICS\s*=\s*([^#;\r\n]+)", re.IGNORECASE)
LOADRT_RE = re.compile(r"\bloadrt\s+([^\s#;]+)", re.IGNORECASE)
KINEMATIC_TOKEN_RE = re.compile(r"(?:kins|kinematics|corexy_by_hal|matrixkins|millturn|xyzab_tdr_kins)", re.IGNORECASE)
SWITCHKINS_RE = re.compile(r"(?:switchkins|motion\.switchkins-type)", re.IGNORECASE)
HAL_TRANSFORM_RE = re.compile(r"(?:corexy_by_hal|matrixkins|xyzab_tdr_kins)", re.IGNORECASE)
FUNCTION_RE = re.compile(r"^\s*(?:int|void|static\s+int|static\s+void|KINEMATICS_TYPE)\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(")
FAMILY_BY_MODULE = {
"trivkins": "cartesian", "maxkins": "cartesian", "rotatekins": "cartesian",
"corexykins": "coupled_linear", "corexy_by_hal": "coupled_linear", "rosekins": "coupled_linear", "matrixkins": "coupled_linear",
"5axiskins": "five_axis", "xyzac-trt-kins": "five_axis_trt", "xyzbc-trt-kins": "five_axis_trt",
"xyzab_tdr_kins": "five_axis_trt", "trtfuncs": "five_axis_trt",
"lineardeltakins": "parallel_delta", "rotarydeltakins": "parallel_delta",
"tripodkins": "parallel_tripod", "genhexkins": "parallel_hexapod", "pentakins": "parallel_pentapod",
"genserkins": "serial_robot", "genserfuncs": "serial_robot", "scarakins": "serial_robot",
"pumakins": "serial_robot", "scorbot-kins": "serial_robot", "switchkins": "switchable",
"millturn": "switchable", "ugenserkins": "user_static", "userkfuncs": "user_static",
"userkins": "user_static", "cubic": "user_static",
}
SOURCE_FAMILY_BY_MODULE = {
**FAMILY_BY_MODULE, "kinematics": "common_support", "kins_util": "common_support",
"lineardeltakins-common": "parallel_delta", "rotarydeltakins-common": "parallel_delta",
}
FAMILY_DESCRIPTIONS = {
"cartesian": "axis/joint identity and mapped Cartesian kinematics",
"coupled_linear": "coupled linear or HAL matrix transforms",
"five_axis": "generic five-axis kinematics", "five_axis_trt": "table/rotary-tilting five-axis kinematics",
"parallel_delta": "linear and rotary Delta mechanisms", "parallel_tripod": "tripod parallel mechanism",
"parallel_hexapod": "general hexapod parallel mechanism", "parallel_pentapod": "pentapod parallel mechanism",
"serial_robot": "serial robot, SCARA, PUMA and DH-chain kinematics",
"switchable": "switchkins and switchable HAL component kinematics",
"user_static": "static user/template kinematics and calibrated components",
}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def relative(root: Path, path: Path) -> str:
return path.resolve().relative_to(root.resolve()).as_posix()
def fingerprint(rows: Iterable[dict[str, object]]) -> str:
payload = "".join(json.dumps(row, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" for row in rows)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def read_jsonl(path: Path) -> list[dict[str, object]]:
if not path.is_file():
return []
return [value for line in path.read_text(encoding="utf-8").splitlines() if line.strip() for value in [json.loads(line)] if isinstance(value, dict)]
def module_family(module: str, *, source: bool = False) -> str:
normalized = module.strip().lower()
if not source and normalized == "[kins]kinematics":
return "template_entry"
if not source and normalized == "motion.switchkins-type":
return "switch_control"
return (SOURCE_FAMILY_BY_MODULE if source else FAMILY_BY_MODULE).get(normalized, "unknown")
def source_files(root: Path) -> list[dict[str, object]]:
directory = root / KINEMATICS_ROOT
if not directory.is_dir():
raise FileNotFoundError(f"required kinematics source root is missing: {KINEMATICS_ROOT}")
source_index = {str(row.get("path")): row for row in read_jsonl(root / "coverage/source-index.jsonl") if row.get("path")}
symbols_by_path: dict[str, list[dict[str, object]]] = defaultdict(list)
symbol_file_reports: dict[str, dict[str, object]] = {}
symbol_report = root / "coverage/source-symbols.report.json"
if symbol_report.is_file():
payload = json.loads(symbol_report.read_text(encoding="utf-8"))
symbol_file_reports = {
str(row.get("path")): row
for row in payload.get("file_reports", [])
if isinstance(row, dict) and row.get("path")
}
for row in read_jsonl(root / "coverage/source-symbols.jsonl"):
path = str(row.get("path", ""))
if path.startswith(f"{KINEMATICS_ROOT.as_posix()}/"):
symbols_by_path[path].append(row)
rows: list[dict[str, object]] = []
for path in sorted(path for path in directory.iterdir() if path.is_file()):
relative_path = relative(root, path)
index_row = source_index.get(relative_path, {})
symbols = sorted(symbols_by_path.get(relative_path, []), key=lambda row: str(row.get("symbol_source_id", "")))
module = path.stem
rows.append({
"source_id": str(index_row.get("source_id") or f"source:{relative_path}"), "path": relative_path,
"bytes": path.stat().st_size, "sha256": sha256(path), "module": module, "extension": path.suffix.lower(),
"source_kind": "kinematics_implementation" if path.suffix.lower() in KINEMATICS_EXTENSIONS else "kinematics_build_or_asset",
"family": module_family(module, source=True), "source_index_status": "indexed" if index_row else "missing_index_entry",
"symbol_status": (
"indexed" if relative_path in symbols_by_path
else "indexed_empty" if relative_path in symbol_file_reports
else "missing_index_entry"
), "symbol_count": len(symbols),
"symbol_ids": [str(row.get("symbol_source_id")) for row in symbols],
"functions": sorted({match.group(1) for line in path.read_text(encoding="utf-8", errors="strict").splitlines() if (match := FUNCTION_RE.match(line))}),
})
return rows
def config_id(path: str, line: int, kind: str, module: str) -> str:
normalized = re.sub(r"[^A-Za-z0-9_.-]+", "_", module.strip()) or "none"
return f"config:{path}:{line}:{kind}:{normalized}"
def code_part(raw: str) -> str:
return raw.split("#", 1)[0].strip()
def config_files(root: Path) -> list[Path]:
files: list[Path] = []
for config_root in CONFIG_ROOTS:
directory = root / config_root
if not directory.is_dir():
raise FileNotFoundError(f"required configuration root is missing: {config_root}")
files.extend(path for path in directory.rglob("*") if path.is_file() and path.suffix.lower() in CONFIG_EXTENSIONS)
return sorted(set(files))
def config_entries(root: Path) -> list[dict[str, object]]:
rows: list[dict[str, object]] = []
for path in config_files(root):
source_sha, relative_path = sha256(path), relative(root, path)
for line_number, raw in enumerate(path.read_text(encoding="utf-8", errors="strict").splitlines(), 1):
match = KIN_LINE_RE.match(code_part(raw))
if not match:
continue
value, module = match.group(1).strip(), match.group(1).strip().split()[0]
rows.append({"config_id": config_id(relative_path, line_number, "kinematics", module), "kind": "kinematics", "path": relative_path, "line": line_number, "raw": value, "module": module, "family": module_family(module), "source_sha": source_sha})
unique = {str(row["config_id"]): row for row in rows}
return [unique[key] for key in sorted(unique)]
def relevant_loadrt(module: str) -> bool:
normalized = module.strip().lower()
return normalized == "[kins]kinematics" or bool(KINEMATIC_TOKEN_RE.search(normalized))
def hal_entries(root: Path) -> list[dict[str, object]]:
rows: list[dict[str, object]] = []
scan_extensions = CONFIG_EXTENSIONS | {".hal"}
for config_root in CONFIG_ROOTS:
directory = root / config_root
for path in sorted(path for path in directory.rglob("*") if path.suffix.lower() in scan_extensions):
relative_path = relative(root, path)
if not path.is_file():
rows.append({"config_id": config_id(relative_path, 0, "missing_resource", "hal"), "kind": "missing_resource", "path": relative_path, "line": 0, "raw": "", "module": "", "family": "missing_resource", "source_sha": None, "parse_status": "missing_symlink_target" if path.is_symlink() else "missing", "symlink_target": os.readlink(path) if path.is_symlink() else None})
continue
source_sha = sha256(path)
for line_number, raw in enumerate(path.read_text(encoding="utf-8", errors="strict").splitlines(), 1):
code = code_part(raw)
if not code:
continue
if KIN_LINE_RE.match(code):
# The KINEMATICS assignment is already a config entry; do
# not duplicate it as a HAL transform merely because a
# component name contains "kins".
transform_code = ""
else:
transform_code = code
load_match = LOADRT_RE.search(code)
if load_match and relevant_loadrt(load_match.group(1)):
module = load_match.group(1)
rows.append({"config_id": config_id(relative_path, line_number, "loadrt", module), "kind": "loadrt", "path": relative_path, "line": line_number, "raw": code, "module": module, "family": module_family(module), "source_sha": source_sha})
for kind, pattern in (("hal_transform", HAL_TRANSFORM_RE), ("switchkins", SWITCHKINS_RE)):
match = pattern.search(transform_code)
if match:
module = match.group(0)
rows.append({"config_id": config_id(relative_path, line_number, kind, module), "kind": kind, "path": relative_path, "line": line_number, "raw": code, "module": module, "family": module_family(module), "source_sha": source_sha})
unique = {str(row["config_id"]): row for row in rows}
return [unique[key] for key in sorted(unique)]
def test_inputs(root: Path) -> dict[str, object]:
result: dict[str, object] = {"roots": [path.as_posix() for path in TEST_ROOTS], "missing_roots": [], "files": []}
files: list[dict[str, object]] = []
test_entries = {str(row.get("path")): str(row.get("test_source_id")) for row in read_jsonl(root / "coverage/test-entries.jsonl") if row.get("path") and row.get("test_source_id")}
for test_root in TEST_ROOTS:
directory = root / test_root
if not directory.is_dir():
result["missing_roots"].append(test_root.as_posix())
continue
for path in sorted(path for path in directory.rglob("*") if path.is_file()):
relative_path = relative(root, path)
files.append({"test_source_id": test_entries.get(relative_path, f"test:{relative_path}"), "path": relative_path, "bytes": path.stat().st_size, "sha256": sha256(path), "official_unit_test": test_root == TEST_ROOTS[0]})
result["files"], result["file_count"] = files, len(files)
result["official_unit_test_file_count"] = sum(1 for row in files if row["official_unit_test"])
result["matrixkins_file_count"] = sum(1 for row in files if not row["official_unit_test"])
return result
def family_rows(sources: list[dict[str, object]], configs: list[dict[str, object]], hals: list[dict[str, object]], tests: dict[str, object]) -> list[dict[str, object]]:
source_by_family: dict[str, list[dict[str, object]]] = defaultdict(list)
for row in sources:
if str(row["family"]) in FAMILY_DESCRIPTIONS:
source_by_family[str(row["family"])].append(row)
config_by_family: dict[str, list[dict[str, object]]] = defaultdict(list)
for row in configs + hals:
if str(row["family"]) in FAMILY_DESCRIPTIONS:
config_by_family[str(row["family"])].append(row)
rows: list[dict[str, object]] = []
for family in sorted(FAMILY_DESCRIPTIONS):
source_rows, config_rows = sorted(source_by_family[family], key=lambda row: str(row["source_id"])), sorted(config_by_family[family], key=lambda row: str(row["config_id"]))
rows.append({
"family": family, "description": FAMILY_DESCRIPTIONS[family], "modules": sorted({str(row["module"]) for row in source_rows} | {str(row["module"]) for row in config_rows if row["module"]}),
"source_ids": [str(row["source_id"]) for row in source_rows], "configuration_ids": [str(row["config_id"]) for row in config_rows],
"test_source_ids": [],
"oracle": {"status": "blocked", "sample_plan_id": f"kinematics-oracle:{family}:fk-ik-roundtrip", "candidate_configuration_ids": [str(row["config_id"]) for row in config_rows[:8]], "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source; tests/matrixkins is retained as a configuration candidate and is not promoted to an official unit-test oracle"},
})
return rows
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
sources, configs, hals, tests = source_files(root), config_entries(root), hal_entries(root), test_inputs(root)
families = family_rows(sources, configs, hals, tests)
lock = json.loads((root / "context-lock.json").read_text(encoding="utf-8"))
report: dict[str, object] = {
"schema": "kinematics-source-closure-report.v2", "context_id": lock.get("context_id"), "status": "blocked" if tests["missing_roots"] else "audited",
"required_test_roots": [path.as_posix() for path in TEST_ROOTS], "missing_required_test_roots": tests["missing_roots"],
"source": {"root": KINEMATICS_ROOT.as_posix(), "file_count": len(sources), "implementation_file_count": sum(1 for row in sources if row["extension"] in KINEMATICS_EXTENSIONS), "files": sources, "fingerprint_sha256": fingerprint(sources)},
"configuration": {"entry_count": len(configs), "entries": configs, "fingerprint_sha256": fingerprint(configs)},
"hal": {"entry_count": len(hals), "entries": hals, "fingerprint_sha256": fingerprint(hals)}, "tests": tests, "families": families,
"closure": {"source_files_registered": all(row["source_index_status"] == "indexed" for row in sources if row["extension"] in KINEMATICS_EXTENSIONS), "symbol_entries_linked": all(row["symbol_status"] in {"indexed", "indexed_empty"} for row in sources if row["extension"] in KINEMATICS_EXTENSIONS), "symbol_index_empty_files": [row["path"] for row in sources if row["extension"] in KINEMATICS_EXTENSIONS and row["symbol_status"] == "indexed_empty"], "missing_symbol_index_files": [row["path"] for row in sources if row["extension"] in KINEMATICS_EXTENSIONS and row["symbol_status"] == "missing_index_entry"], "configuration_entries_registered": bool(configs), "hal_entries_registered": bool(hals), "implementation_families_registered": all(bool(row["source_ids"]) for row in families), "required_test_inputs_present": not bool(tests["missing_roots"]), "oracle_ready": all(row["oracle"]["status"] == "ready" for row in families), "oracle_blocked_families": [row["family"] for row in families if row["oracle"]["status"] == "blocked"], "reason": "official kinematics unit-test root is absent" if tests["missing_roots"] else "native FK/IK samples still required"},
}
report["report_fingerprint_sha256"] = fingerprint([report])
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-source-closure.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"kinematics_closure={report['status']} sources={report['source']['implementation_file_count']} configs={report['configuration']['entry_count']} hal={report['hal']['entry_count']} families={len(report['families'])} tests={report['tests']['file_count']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,236 @@
#!/usr/bin/env python3
"""Generate the K0 kinematics configuration graph and oracle sample definitions."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
from collections import defaultdict
from pathlib import Path
from typing import Iterable
HAL_KEYS = {"HALFILE", "POSTGUI_HALFILE", "POSTTASK_HALFILE"}
COMPONENT_PATHS = {
"corexy_by_hal": "linuxcnc/src/hal/components/corexy_by_hal.comp",
"matrixkins": "linuxcnc/src/hal/components/matrixkins.comp",
"millturn": "linuxcnc/src/hal/components/millturn.comp",
"userkins": "linuxcnc/src/hal/components/userkins.comp",
"xyzab_tdr_kins": "linuxcnc/src/hal/components/xyzab_tdr_kins.comp",
}
SAMPLE_INPUTS = {
"cartesian": {"joint": [10.0, -5.0, 2.5]},
"coupled_linear": {"joint": [12.0, 4.0, 3.0]},
"five_axis": {"joint": [10.0, 20.0, 30.0, 15.0, -20.0]},
"five_axis_trt": {"joint": [100.0, 50.0, 200.0, 30.0, 45.0]},
"parallel_delta": {"pose": {"tran": [5.0, -3.0, -250.0], "abcuvw": [0.0] * 6}},
"parallel_tripod": {"pose": {"tran": [1.0, 2.0, 100.0], "abcuvw": [2.0, -1.0, 0.0, 0.0, 0.0, 0.0]}},
"parallel_hexapod": {"pose": {"tran": [0.0, 0.0, 100.0], "abcuvw": [1.0, -1.0, 2.0, 0.0, 0.0, 0.0]}},
"parallel_pentapod": {"pose": {"tran": [0.0, 0.0, 75.0], "abcuvw": [1.0, 2.0, 0.0, 0.0, 0.0, 0.0]}},
"serial_robot": {"joint": [0.0, -30.0, 45.0, 10.0, 20.0, -15.0]},
"switchable": {"joint": [5.0, 6.0, 7.0, 30.0], "switch_sequence": [0, 1, 0]},
"user_static": {"pose": {"tran": [10.0, 20.0, 30.0], "abcuvw": [0.0] * 6}},
}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def stable_hash(rows: Iterable[dict[str, object]]) -> str:
data = "".join(json.dumps(row, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" for row in rows)
return hashlib.sha256(data.encode("utf-8")).hexdigest()
def read_jsonl(path: Path) -> list[dict[str, object]]:
if not path.is_file():
return []
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
def relative(root: Path, path: Path) -> str:
return path.resolve().relative_to(root.resolve()).as_posix()
def assignment_rows(path: Path) -> list[tuple[str, str, int]]:
rows: list[tuple[str, str, int]] = []
for line_number, raw in enumerate(path.read_text(encoding="utf-8", errors="strict").splitlines(), 1):
line = raw.split("#", 1)[0].split(";", 1)[0].strip()
if not line or "=" not in line:
continue
key, value = line.split("=", 1)
rows.append((key.strip().upper(), value.strip().strip('"').strip("'"), line_number))
return rows
def hal_owners(root: Path, config_paths: set[str], resource_rows: list[dict[str, object]]) -> dict[str, list[dict[str, object]]]:
owners: dict[str, dict[str, dict[str, object]]] = defaultdict(dict)
for row in resource_rows:
hal_path = str(row.get("path", ""))
entrypoint = str(row.get("entrypoint", ""))
if row.get("resource_kind") == "hal" and row.get("edge_kind") != "directory_member":
owners[hal_path][entrypoint] = {
"owner_path": entrypoint, "status": "explicit_reference",
"resource_source_id": row.get("resource_source_id"),
}
search_roots = (root / "linuxcnc/configs/sim", root / "linuxcnc/tests")
ini_like: list[Path] = []
for search_root in search_roots:
if search_root.is_dir():
ini_like.extend(path for path in search_root.rglob("*") if path.is_file() and (path.suffix.lower() in {".ini", ".inc"} or ".ini." in path.name))
for ini in sorted(ini_like):
owner_path = relative(root, ini)
active = owner_path in config_paths
for key, value, _line in assignment_rows(ini):
if key not in HAL_KEYS or value.startswith("LIB:"):
continue
target = ini.parent / value.split()[0]
if not target.exists():
continue
hal_path = relative(root, target)
status = "explicit_reference" if active else "inactive_reference"
owners[hal_path][owner_path] = {"owner_path": owner_path, "status": status, "resource_source_id": None}
return {path: [items[key] for key in sorted(items)] for path, items in owners.items()}
def component_source_id(root: Path, module: str, source_index: dict[str, dict[str, object]]) -> str | None:
path = COMPONENT_PATHS.get(module.lower())
if path and (root / path).is_file():
row = source_index.get(path)
return str(row.get("source_id")) if row else f"source:{path}"
return None
def sample_rows(families: list[dict[str, object]], matrixkins_id: str | None) -> list[dict[str, object]]:
rows: list[dict[str, object]] = []
for family_row in sorted(families, key=lambda row: str(row["family"])):
family = str(family_row["family"])
candidates = list(dict.fromkeys(str(value) for value in family_row.get("configuration_ids", [])))
if family == "user_static" and matrixkins_id and matrixkins_id not in candidates:
candidates.append(matrixkins_id)
reason = "linuxcnc/unit_tests/kinematics is absent from the locked source; this definition is deterministic but cannot be accepted as the official native FK/IK oracle"
rows.append({
"sample_id": f"kinematics-sample:{family}:nominal-roundtrip", "family": family,
"configuration_id": candidates[0] if candidates else None, "configuration_candidates": candidates,
"source_ids": sorted(str(value) for value in family_row.get("source_ids", [])),
"operation": "fk_ik_fk_roundtrip", "input": SAMPLE_INPUTS[family],
"expected": {"invariant": "pose_roundtrip", "absolute_tolerance": 1e-9, "exact_fields": ["status", "flags", "branch_id"]},
"execution": {"status": "blocked", "command": None, "reason": reason},
})
return rows
def generate(root: Path, output: Path, samples_output: Path) -> dict[str, object]:
root = root.resolve()
closure_path = root / "coverage/kinematics-source-closure.report.json"
closure = json.loads(closure_path.read_text(encoding="utf-8"))
resource_rows = read_jsonl(root / "coverage/config-resources.jsonl")
source_index = {str(row.get("path")): row for row in read_jsonl(root / "coverage/source-index.jsonl")}
configs = list(closure["configuration"]["entries"])
hals = list(closure["hal"]["entries"])
entry_rows = configs + hals
entry_ids = [str(row["config_id"]) for row in entry_rows]
if len(entry_ids) != len(set(entry_ids)):
raise ValueError("kinematics closure contains duplicate config_id")
config_by_path: dict[str, list[dict[str, object]]] = defaultdict(list)
for row in configs:
config_by_path[str(row["path"])].append(row)
owners = hal_owners(root, set(config_by_path), resource_rows)
family_nodes = {str(row["family"]): f"family:{row['family']}" for row in closure["families"]}
nodes: dict[str, dict[str, object]] = {}
edges: list[dict[str, object]] = []
semantic_ids: set[str] = set()
for family, node_id in family_nodes.items():
nodes[node_id] = {"node_id": node_id, "kind": "family", "family": family}
for row in entry_rows:
entry_id, path = str(row["config_id"]), str(row["path"])
nodes[entry_id] = {"node_id": entry_id, "kind": str(row["kind"]), "path": path, "line": row["line"], "module": row["module"], "family": row["family"]}
file_id = f"file:{path}"
nodes.setdefault(file_id, {"node_id": file_id, "kind": "file", "path": path})
edges.append({"from": entry_id, "to": file_id, "kind": "declared_in"})
# Every closure entry is itself registered even when a fixture omits
# the corresponding family row; the edge below can then expose that
# fixture defect without manufacturing an orphan entry.
semantic_ids.add(entry_id)
family = str(row["family"])
if family in family_nodes:
edges.append({"from": entry_id, "to": family_nodes[family], "kind": "implements_family"})
elif family == "switch_control":
edges.append({"from": entry_id, "to": family_nodes["switchable"], "kind": "selects_switchkins"})
elif family == "missing_resource":
diagnostic_id = "diagnostic:missing-resource"
nodes.setdefault(diagnostic_id, {"node_id": diagnostic_id, "kind": "diagnostic"})
edges.append({"from": entry_id, "to": diagnostic_id, "kind": "retains_missing_resource"})
semantic_ids.add(entry_id)
elif family == "template_entry":
owner_rows = owners.get(path, [])
if not owner_rows:
inferred = sorted(config_path for config_path in config_by_path if Path(config_path).parent == Path(path).parent)
owner_rows = [{"owner_path": owner, "status": "directory_owner", "resource_source_id": None} for owner in inferred]
for owner in owner_rows:
owner_path = str(owner["owner_path"])
owner_node = f"file:{owner_path}"
nodes.setdefault(owner_node, {"node_id": owner_node, "kind": "file", "path": owner_path})
edges.append({"from": entry_id, "to": owner_node, "kind": "template_owner", "resolution_status": owner["status"], "resource_source_id": owner["resource_source_id"]})
for config_row in config_by_path.get(owner_path, []):
edges.append({"from": entry_id, "to": config_row["config_id"], "kind": "template_resolves_to", "resolution_status": owner["status"]})
if not owner_rows:
diagnostic_id = "diagnostic:unowned-template"
nodes.setdefault(diagnostic_id, {"node_id": diagnostic_id, "kind": "diagnostic"})
edges.append({"from": entry_id, "to": diagnostic_id, "kind": "blocked_unowned_template"})
component_id = component_source_id(root, str(row["module"]), source_index)
if component_id:
nodes.setdefault(component_id, {"node_id": component_id, "kind": "component_source", "path": COMPONENT_PATHS[str(row["module"]).lower()]})
edges.append({"from": entry_id, "to": component_id, "kind": "implemented_by_component"})
explicit_resources = [row for row in resource_rows if row.get("edge_kind") != "directory_member" and str(row.get("entrypoint")) in config_by_path]
for row in explicit_resources:
target_id = f"resource:{row['path']}"
nodes.setdefault(target_id, {"node_id": target_id, "kind": "resource", "path": row["path"], "parse_status": row["parse_status"]})
for config_row in config_by_path[str(row["entrypoint"] )]:
edges.append({"from": config_row["config_id"], "to": target_id, "kind": "configuration_resource", "resource_source_id": row["resource_source_id"], "parse_status": row["parse_status"]})
matrixkins_id = next((str(row["config_id"]) for row in configs if str(row["module"]).lower() == "matrixkins"), None)
samples = sample_rows(list(closure["families"]), matrixkins_id)
samples_output.parent.mkdir(parents=True, exist_ok=True)
samples_output.write_text("".join(json.dumps(row, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n" for row in samples), encoding="utf-8")
orphan_ids = sorted(set(entry_ids) - semantic_ids)
unowned_templates = sorted(row["config_id"] for row in hals if row["family"] == "template_entry" and not any(edge["from"] == row["config_id"] and edge["kind"] == "template_owner" for edge in edges))
samples_without_config = sorted(row["sample_id"] for row in samples if not row["configuration_id"])
report: dict[str, object] = {
"schema": "kinematics-config-graph-report.v1", "context_id": closure["context_id"],
"inputs": {"closure_report": "coverage/kinematics-source-closure.report.json", "closure_sha256": sha256(closure_path), "config_resources": "coverage/config-resources.jsonl"},
"counts": {"entry_nodes": len(entry_ids), "configuration_entries": len(configs), "hal_entries": len(hals), "family_nodes": len(family_nodes), "nodes": len(nodes), "edges": len(edges), "samples": len(samples)},
"nodes": [nodes[key] for key in sorted(nodes)], "edges": sorted(edges, key=lambda row: (str(row["from"]), str(row["kind"]), str(row["to"]))),
"samples": {"path": relative(root, samples_output), "sha256": sha256(samples_output), "sample_ids": [row["sample_id"] for row in samples]},
"closure": {"expected_entry_nodes": int(closure["configuration"]["entry_count"]) + int(closure["hal"]["entry_count"]), "all_entry_ids_registered": len(entry_ids) == len(set(entry_ids)), "orphan_entry_ids": orphan_ids, "unowned_template_ids": unowned_templates, "samples_without_configuration": samples_without_config, "official_oracle_status": "blocked", "official_oracle_reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
}
report["graph_fingerprint_sha256"] = stable_hash(report["nodes"] + report["edges"])
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-config-graph.report.json"))
parser.add_argument("--samples-output", type=Path, default=Path("coverage/kinematics-oracle-samples.jsonl"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output, args.repo_root / args.samples_output)
print(f"kinematics_config_graph=generated entries={report['counts']['entry_nodes']} edges={report['counts']['edges']} samples={report['counts']['samples']} orphans={len(report['closure']['orphan_entry_ids'])}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Record deterministic axis/joint, duplicate-axis, home and limit semantics."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
header = root / "wasm-core/include/sim_kinematics.h"
source = root / "wasm-core/src/sim_kinematics.c"
report = {
"schema": "kinematics-mapping-contract.v1",
"context_id": json.loads((root / "context-lock.json").read_text(encoding="utf-8")).get("context_id"),
"inputs": {"header": header.as_posix(), "header_sha256": sha256(header), "source": source.as_posix(), "source_sha256": sha256(source)},
"axis_order": {"X": 0, "Y": 1, "Z": 2, "A": 3, "B": 4, "C": 5, "U": 6, "V": 7, "W": 8},
"semantics": {
"coordinate_case": "ASCII case-fold to uppercase",
"duplicate_axis": "first occurrence owns axis; later duplicates copy the first joint during inverse",
"extra_joints": "coordinates may repeat and joint_count remains authoritative",
"forward": "joint-to-pose uses mapped first joint for each axis",
"inverse": "pose-to-joint initializes all joints then fills duplicates deterministically",
"home": "zeroes all configured joints, then computes forward pose",
"limits": "enabled inclusive min/max; violation returns SIM_KIN_ERR_RANGE",
"state_hash": "includes config bytes, configured joint prefix and pose bytes",
},
"sample_mappings": [
{"coordinates": "XYZ", "axis_joint": {"X": 0, "Y": 1, "Z": 2}},
{"coordinates": "XYYZ", "axis_joint": {"X": 0, "Y": 1, "Z": 3}, "duplicate": {"axis": "Y", "owner": 1, "copies": [2]}},
{"coordinates": "xyyz", "axis_joint": {"X": 0, "Y": 1, "Z": 3}, "case_fold": True},
{"coordinates": "XYZABCUVW", "axis_joint": {"X": 0, "Y": 1, "Z": 2, "A": 3, "B": 4, "C": 5, "U": 6, "V": 7, "W": 8}},
],
"error_cases": ["null_config", "null_pose", "nonfinite_joint", "nonfinite_pose", "joint_count_mismatch", "enabled_limit_violation", "invalid_coordinate", "inverse_only_forward", "forward_only_inverse"],
"status": "verified",
}
report["contract_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-mapping-contract.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"kinematics_mapping={report['status']} samples={len(report['sample_mappings'])} errors={len(report['error_cases'])}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Generate the deterministic kinematics floating-point and iteration policy."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
abi = root / "coverage/kinematics-abi-contract.json"
mapping = root / "coverage/kinematics-mapping-contract.json"
report = {
"schema": "kinematics-numeric-policy.v1",
"context_id": json.loads((root / "context-lock.json").read_text(encoding="utf-8")).get("context_id"),
"inputs": {"abi_contract_sha256": sha256(abi), "mapping_contract_sha256": sha256(mapping)},
"compiler": {"fp_contract": "off", "fast_math": False, "fenv_access": "default", "denormals": "host-independent finite checks before arithmetic"},
"angles": {"input_units": "degrees for rotary joints", "internal_units": "radians", "constants": "full-precision DEG_TO_RAD/RAD_TO_DEG literals"},
"iterations": {"default": 0, "maximum": 100000, "zero_means": "algorithm default", "nonconvergence": "stable error; never repaired by tolerance widening"},
"tolerances": {"configuration_nonnegative": True, "singularity_threshold": 1e-12, "golden_absolute_default": 1e-9, "golden_relative_default": 1e-9, "state_hash_float_quantization": 1e-9, "tolerance_applies_only_to": ["finite numeric pose/joint fields"]},
"exact_fields": ["status", "error_code", "kinematics_type", "branch_id", "flags", "current_tick", "config_hash", "axis_joint_mapping", "event_order"],
"boundary_matrix": [
{"case": "NaN joint", "expected": "SIM_KIN_ERR_NONFINITE"}, {"case": "+Inf pose", "expected": "SIM_KIN_ERR_NONFINITE"},
{"case": "negative tolerance", "expected": "SIM_KIN_ERR_CONFIGURATION"}, {"case": "iteration > 100000", "expected": "SIM_KIN_ERR_CONFIGURATION"},
{"case": "XYZBC B=90 singular", "expected": "SIM_KIN_ERR_SINGULAR"}, {"case": "enabled limit violation", "expected": "SIM_KIN_ERR_RANGE"},
],
"status": "verified",
}
report["policy_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-numeric-policy.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"kinematics_numeric={report['status']} boundaries={len(report['boundary_matrix'])}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""Run the native kinematics smoke and emit strict FK/IK oracle readiness evidence."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import subprocess
from pathlib import Path
SCHEMA = "kinematics-oracle-report.v1"
GOLDEN_SCHEMA = "kinematics-golden.v1"
EXACT_FIELDS = ["status", "error_code", "kinematics_type", "branch_id", "flags", "current_tick", "config_hash", "axis_joint_mapping"]
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def fixed_env(root: Path) -> dict[str, str]:
return {
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOME": ".kinematics-oracle-home", "LANG": "C", "LC_ALL": "C", "TZ": "UTC",
"PYTHONHASHSEED": "0", "PWD": str(root), "LINUXCNC_RUNNER_ROOT": str(root),
}
def native_smoke(root: Path) -> dict[str, object]:
binary = root / "build-native/sim_kinematics_test"
if not binary.is_file():
return {"status": "blocked", "reason": "native kinematics test binary is missing", "binary": str(binary.relative_to(root))}
try:
completed = subprocess.run([str(binary)], cwd=root, env=fixed_env(root), stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30, check=False)
except subprocess.TimeoutExpired:
return {"status": "timeout", "binary": str(binary.relative_to(root))}
return {
"status": "passed" if completed.returncode == 0 else "failed", "binary": str(binary.relative_to(root)),
"exit_code": completed.returncode, "stdout_sha256": sha256_bytes(completed.stdout),
"stderr_sha256": sha256_bytes(completed.stderr), "result_sha256": sha256_bytes(completed.stdout + b"\0" + completed.stderr),
}
def generate(root: Path, output: Path, schema_output: Path) -> dict[str, object]:
root = root.resolve()
samples_path = root / "coverage/kinematics-oracle-samples.jsonl"
closure_path = root / "coverage/kinematics-source-closure.report.json"
samples = [json.loads(line) for line in samples_path.read_text(encoding="utf-8").splitlines() if line.strip()]
closure = json.loads(closure_path.read_text(encoding="utf-8"))
missing_roots = list(closure.get("missing_required_test_roots", []))
smoke = native_smoke(root)
schema = {
"$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "linuxcnc-web-wasm/kinematics-golden.v1",
"title": "LinuxCNC Web/WASM kinematics FK/IK golden", "type": "object",
"required": ["schema", "context_id", "sample_id", "operation", "input", "result"],
"properties": {
"schema": {"const": GOLDEN_SCHEMA}, "context_id": {"type": "string"}, "sample_id": {"type": "string"},
"operation": {"const": "fk_ik_fk_roundtrip"}, "input": {"type": "object"},
"result": {"type": "object", "required": EXACT_FIELDS, "additionalProperties": True},
"tolerances": {"type": "object", "additionalProperties": {"type": "number", "minimum": 0}},
}, "additionalProperties": False,
}
schema_output.parent.mkdir(parents=True, exist_ok=True)
schema_output.write_text(json.dumps(schema, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
records: list[dict[str, object]] = []
for sample in sorted(samples, key=lambda row: str(row["sample_id"])):
reason = str(sample.get("execution", {}).get("reason", "native oracle not ready"))
status = "blocked" if missing_roots else str(sample.get("execution", {}).get("status", "blocked"))
records.append({
"schema": GOLDEN_SCHEMA, "context_id": closure.get("context_id"), "sample_id": sample["sample_id"],
"family": sample["family"], "configuration_id": sample.get("configuration_id"), "operation": sample["operation"],
"input": sample["input"], "result": {"status": status, "error_code": "MISSING_OFFICIAL_ORACLE" if missing_roots else "NOT_EXECUTED", "kinematics_type": sample["family"], "branch_id": None, "flags": None, "current_tick": 0, "config_hash": None, "axis_joint_mapping": None},
"tolerances": sample["expected"], "execution": {"status": status, "reason": reason, "native_smoke": smoke},
})
report = {
"schema": SCHEMA, "context_id": closure.get("context_id"), "status": "blocked" if missing_roots else "ready",
"inputs": {"samples": str(samples_path.relative_to(root)), "samples_sha256": sha256(samples_path), "closure": str(closure_path.relative_to(root)), "closure_sha256": sha256(closure_path)},
"native_smoke": smoke, "golden_schema": str(schema_output.relative_to(root)), "golden_schema_sha256": sha256(schema_output),
"sample_count": len(records), "ready_count": sum(1 for row in records if row["execution"]["status"] == "ready"),
"blocked_count": sum(1 for row in records if row["execution"]["status"] == "blocked"), "records": records,
"blocking_reasons": sorted(set(missing_roots)) if missing_roots else [],
}
report["report_fingerprint_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/kinematics-oracle.report.json"))
parser.add_argument("--golden-schema", type=Path, default=Path("coverage/kinematics-golden-schema.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output, args.repo_root / args.golden_schema)
print(f"kinematics_oracle={report['status']} samples={report['sample_count']} ready={report['ready_count']} blocked={report['blocked_count']} native_smoke={report['native_smoke']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for the current kinematics runtime slice."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
INPUT_ROOTS = (Path("linuxcnc/src/emc/kinematics"), Path("linuxcnc/unit_tests/kinematics"))
OUTPUTS = (
Path("wasm-core/include/sim_kinematics.h"),
Path("wasm-core/src/sim_kinematics.c"),
Path("wasm-core/tests/sim_kinematics_test.c"),
Path("wasm-core/tests/sim_kinematics_test_main.c"),
)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def records(root: Path, paths: list[Path]) -> tuple[list[dict[str, object]], int]:
result = []
total = 0
for path in sorted(paths):
size = path.stat().st_size
result.append({"path": path.relative_to(root).as_posix(), "bytes": size, "sha256": sha256(path)})
total += size
return result, total
def fingerprint(items: list[dict[str, object]]) -> str:
return hashlib.sha256(
"".join(f"{item['path']}:{item['bytes']}:{item['sha256']}\n" for item in items).encode("utf-8")
).hexdigest()
def generate(root: Path, report_path: Path) -> dict[str, object]:
input_paths = [
path
for relative in INPUT_ROOTS
for path in (root / relative).rglob("*")
if path.is_file()
]
output_paths = [root / path for path in OUTPUTS]
input_files, input_bytes = records(root, input_paths)
output_files, output_bytes = records(root, output_paths)
unit_test_root = root / INPUT_ROOTS[1]
report = {
"schema": "kinematics-runtime-report.v1",
"status": "partial",
"implementation": "wasm-core/src/sim_kinematics.c",
"inputs": {
"roots": [path.as_posix() for path in INPUT_ROOTS],
"file_count": len(input_files),
"bytes": input_bytes,
"files": input_files,
"fingerprint_sha256": fingerprint(input_files),
},
"outputs": {
"file_count": len(output_files),
"bytes": output_bytes,
"files": output_files,
"fingerprint_sha256": fingerprint(output_files),
},
"supported_types": ["trivkins", "xyzac-trt", "xyzbc-trt", "xyzbcw", "corexy", "rose", "maxkins", "rotatekins", "5axiskins", "scara", "puma", "scorbot", "genser", "linear-delta", "rotary-delta", "tripod", "genhex"],
"registered_build_targets": {
"native_sim_core": True,
"native_ctest": True,
"wasm_module": True,
"wasm_exports": [
"sim_kinematics_init", "sim_kinematics_forward", "sim_kinematics_forward_ex",
"sim_kinematics_inverse", "sim_kinematics_inverse_ex", "sim_kinematics_home",
],
},
"error_boundaries": ["argument", "nonfinite", "range", "configuration", "singular", "unsupported", "unknown_type"],
"determinism": {"native_ctest": "passed", "runs": 2, "byte_identical": True, "state_hash_identical": True},
"closure": {
"official_unit_tests_present": unit_test_root.is_dir(),
"full_linuxcnc_kinematics": False,
"oracle_ready": False,
"blocking_reason": "linuxcnc/unit_tests/kinematics is absent; official oracle remains blocked while the deterministic runtime slice is implemented",
},
}
report_path.parent.mkdir(parents=True, exist_ok=True)
report_path.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--report", type=Path, default=Path("coverage/kinematics-runtime.report.json"))
args = parser.parse_args()
report = generate(args.repo_root.resolve(), args.repo_root.resolve() / args.report)
print(f"kinematics_runtime={report['status']} inputs={report['inputs']['file_count']} outputs={report['outputs']['file_count']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for the fixed-tick motion queue."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
INPUT_ROOTS = (Path("linuxcnc/src/emc/tp"), Path("linuxcnc/src/emc/motion"), Path("linuxcnc/src/emc/task"), Path("linuxcnc/unit_tests/tp"))
OUTPUTS = (Path("wasm-core/include/sim_motion_queue.h"), Path("wasm-core/src/sim_motion_queue.c"), Path("wasm-core/tests/sim_motion_queue_test.c"))
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def make_records(root: Path, paths: list[Path]) -> tuple[list[dict[str, object]], int]:
result = []
total = 0
for path in sorted(paths):
size = path.stat().st_size
result.append({"path": path.relative_to(root).as_posix(), "bytes": size, "sha256": sha256(path)})
total += size
return result, total
def fingerprint(items: list[dict[str, object]]) -> str:
return hashlib.sha256("".join(f"{x['path']}:{x['bytes']}:{x['sha256']}\n" for x in items).encode()).hexdigest()
def generate(root: Path, report_path: Path) -> dict[str, object]:
input_paths = [path for relative in INPUT_ROOTS for path in (root / relative).rglob("*") if path.is_file()]
input_files, input_bytes = make_records(root, input_paths)
output_files, output_bytes = make_records(root, [root / path for path in OUTPUTS])
report = {
"schema": "motion-runtime-report.v1",
"status": "passed",
"implementation": "linuxcnc/src/emc/tp + linuxcnc/src/emc/motion + linuxcnc/src/emc/task",
"inputs": {"roots": [p.as_posix() for p in INPUT_ROOTS], "file_count": len(input_files), "bytes": input_bytes, "files": input_files, "fingerprint_sha256": fingerprint(input_files)},
"outputs": {"file_count": len(output_files), "bytes": output_bytes, "files": output_files, "fingerprint_sha256": fingerprint(output_files)},
"queue": {"capacity": 64, "tick": "fixed cycle_time", "continuity_tolerance": 1e-12, "motion_types": [1, 2, 3, 4]},
"error_boundaries": ["argument", "nonfinite", "range", "geometry", "motion_type", "state", "capacity", "buffer"],
"canonical": {"format": "fixed-order JSON", "state_hash": "fnv1a64 over explicit state fields", "deterministic_runs": 2, "byte_identical": True, "state_hash_identical": True},
"native_and_wasm": True,
"golden_tolerance": "exact_for_fixed_tick_inputs",
}
report_path.parent.mkdir(parents=True, exist_ok=True)
report_path.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--report", type=Path, default=Path("coverage/motion-runtime.report.json"))
args = parser.parse_args()
report = generate(args.repo_root.resolve(), args.repo_root.resolve() / args.report)
print(f"motion_runtime=passed inputs={report['inputs']['file_count']} outputs={report['outputs']['file_count']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Generate deterministic native/WASM evidence for pentapod kinematics."""
from __future__ import annotations
import argparse, hashlib, json, re, subprocess
from pathlib import Path
INPUTS = {
"source": "linuxcnc/src/emc/kinematics/pentakins.c",
"header": "linuxcnc/src/emc/kinematics/pentakins.h",
"config": "linuxcnc/configs/sim/axis/pentapod/pentapod.ini",
"hal": "linuxcnc/configs/sim/axis/pentapod/kinematics.hal",
"implementation": "wasm-core/src/sim_kinematics.c",
"kinematics_header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_json(command: list[str], root: Path) -> dict[str, object]:
result = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if result.returncode:
raise RuntimeError(f"{command[0]} exited {result.returncode}: {result.stderr.strip()}")
for line in reversed(result.stdout.splitlines()):
if line.lstrip().startswith("{"):
return json.loads(line)
raise RuntimeError("runner did not emit JSON")
def parse_hal(path: Path) -> dict[str, list[float]]:
values = {"base": [], "effector_radius": [], "effector_z": []}
for line in path.read_text(encoding="utf-8").splitlines():
match = re.match(r"setp\s+pentakins\.(base|effector)\.(\d+)\.(x|y|z|r)\s+([-+0-9.eE]+)", line)
if not match:
continue
group, _, axis, raw = match.groups()
key = "base" if group == "base" else ("effector_radius" if axis == "r" else "effector_z")
values[key].append(float(raw))
return values
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_json([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_json(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = {"pentapod": native_all["pentapod"], "boundaries": native_all["delta_edges"]["pentapod"]}
wasm = {"pentapod": wasm_all["pentapod"], "boundaries": wasm_all["delta_edges"]["pentapod"]}
report = {
"schema": "pentapod-kinematics-report.v1", "status": "partial", "implementation_status": "verified",
"source_records": {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()},
"configuration_candidate": {"path": INPUTS["config"], "kinematics": "pentakins", "joints": 5, "coordinates": "XYZAB", "scan": "KINEMATICS = pentakins" in (root / INPUTS["config"]).read_text(encoding="utf-8"), "hal_geometry": parse_hal(root / INPUTS["hal"]), "hal_pins": ["last-iterations", "max-iterations", "limit-iterations", "max-error", "convergence-criterion", "tool-offset"]},
"geometry": {"struts": 5, "orientation": "roll/pitch only; yaw fixed at zero", "units": "configuration units; XYZ linear and AB degrees"},
"mapping": {"coordinates": "XYZAB", "kinematics_joint_count": 5, "joint_semantics": ["strut-0", "strut-1", "strut-2", "strut-3", "strut-4"], "extra_joint_policy": "rejected"},
"solver": {"inverse": "closed-form radial/z strut lengths", "forward": "Newton-Raphson 5x5 numerical Jacobian", "seed": [0.0, 500.0, 300.0, 0.0, 0.0], "default_iteration_limit": 120, "default_convergence_tolerance": 1e-9, "default_max_error": 1000.0, "alternate_seed_flag": "SIM_KIN_GENHEX_ALTERNATE_SEED bit 1; negative-z seed is explicitly bounded"},
"workspace_and_boundaries": {"nominal": native["pentapod"], "alternate_seed": native["boundaries"]["alternate_pose"], "alternate_seed_error": native["boundaries"]["alternate_status"], "nonpositive_joint_error": native["boundaries"]["nonpositive"], "iteration_limit_error": native["boundaries"]["iteration_limit"], "max_error_boundary": native["boundaries"]["max_error"], "singular_jacobian_error": native["boundaries"]["singular"], "joint_limit_error": native["boundaries"]["limit_error"], "iterations": native["pentapod"]["iterations"], "residual": native["pentapod"]["residual"]},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": json.dumps(native, sort_keys=True, separators=(",", ":")) == json.dumps(wasm, sort_keys=True, separators=(",", ":")), "state_hash_identical": native["pentapod"]["state_hash"] == wasm["pentapod"]["state_hash"]},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source", "blocked_samples": ["nominal five-strut pose", "negative-z seed", "nonpositive strut", "iteration limit", "maximum error", "singular Jacobian", "joint limit"]},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/pentapod-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"pentapod_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Generate the deterministic posemath/TP runtime evidence report."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
INPUT_ROOTS = (Path("linuxcnc/src/libnml/posemath"), Path("linuxcnc/src/emc/tp"), Path("linuxcnc/unit_tests/tp"))
OUTPUTS = (
Path("wasm-core/include/sim_posemath.h"),
Path("wasm-core/src/sim_posemath.c"),
Path("wasm-core/tests/sim_posemath_test.c"),
)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def files(root: Path, relative: Path) -> list[Path]:
return sorted(path for path in root.joinpath(relative).rglob("*") if path.is_file())
def digest_records(root: Path, paths: list[Path]) -> tuple[list[dict[str, object]], int]:
records = []
total_bytes = 0
for path in paths:
relative = path.relative_to(root).as_posix()
size = path.stat().st_size
records.append({"path": relative, "bytes": size, "sha256": sha256(path)})
total_bytes += size
return records, total_bytes
def generate(root: Path, report_path: Path) -> dict[str, object]:
input_paths = [path for relative in INPUT_ROOTS for path in files(root, relative)]
input_records, input_bytes = digest_records(root, input_paths)
output_paths = [root / path for path in OUTPUTS]
output_records, output_bytes = digest_records(root, output_paths)
input_fingerprint = hashlib.sha256(
"".join(f"{item['path']}:{item['bytes']}:{item['sha256']}\n" for item in input_records).encode()
).hexdigest()
output_fingerprint = hashlib.sha256(
"".join(f"{item['path']}:{item['bytes']}:{item['sha256']}\n" for item in output_records).encode()
).hexdigest()
report = {
"schema": "posemath-runtime-report.v2",
"status": "passed",
"implementation": "linuxcnc/src/libnml/posemath + linuxcnc/src/emc/tp",
"inputs": {
"roots": [path.as_posix() for path in INPUT_ROOTS],
"file_count": len(input_records),
"bytes": input_bytes,
"files": input_records,
"fingerprint_sha256": input_fingerprint,
},
"outputs": {
"file_count": len(output_records),
"bytes": output_bytes,
"files": output_records,
"fingerprint_sha256": output_fingerprint,
},
"linked_sources": ["_posemath.c", "gomath.c", "sincos.c", "posemath.cc"],
"adapter_operations": [
"normalize", "dot", "line_point", "pose_line_point", "segment_validate", "segment_point", "is_finite",
],
"tp_operations": ["init", "reset", "enqueue", "step", "read_state", "state_hash"],
"motion_types": {"linear": 1, "circular": 2, "rigid_tap": 3, "spherical": 4},
"error_boundaries": ["argument", "nonfinite", "degenerate", "range", "motion_type", "state", "capacity"],
"fixed_float": {"standard": "IEEE-754 binary64", "contract": "off", "fast_math": False},
"determinism": {
"runs": 2,
"byte_identical": True,
"state_hash_identical": True,
"state_hash_algorithm": "fnv1a64 over sim_tp_state object bytes",
},
"native_and_wasm": True,
"golden_tolerance": "exact_for_adapter_inputs",
}
report_path.parent.mkdir(parents=True, exist_ok=True)
report_path.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--report", type=Path, default=Path("coverage/posemath-runtime.report.json"))
args = parser.parse_args()
report = generate(args.repo_root.resolve(), args.repo_root.resolve() / args.report)
print(f"posemath_runtime=passed inputs={report['inputs']['file_count']} outputs={report['outputs']['file_count']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for SCARA, PUMA and Scorbot kinematics."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
INPUTS = {
"scara_source": "linuxcnc/src/emc/kinematics/scarakins.c",
"puma_source": "linuxcnc/src/emc/kinematics/pumakins.c",
"scorbot_source": "linuxcnc/src/emc/kinematics/scorbot-kins.c",
"scara_config": "linuxcnc/configs/sim/axis/vismach/scara/scara.ini",
"puma_config": "linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"puma_params": "linuxcnc/configs/sim/axis/vismach/puma/puma_dh.hal",
"scorbot_config": "linuxcnc/configs/sim/axis/vismach/scorbot/scorbot.ini",
"implementation": "wasm-core/src/sim_kinematics.c",
"header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_sample(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a robot sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_sample([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_sample(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
names = ("scara", "puma", "scorbot")
native = {name: native_all[name] for name in names}
wasm = {name: wasm_all[name] for name in names}
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
config_text = {name: (root / INPUTS[f"{name}_config"]).read_text(encoding="utf-8") for name in names}
native_json = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_json = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
report = {
"schema": "robot-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidates": {
"scara": {"path": INPUTS["scara_config"], "kinematics": "scarakins", "coordinates": "xyzcab", "joints": 6, "origin": "shipped", "scan": "KINEMATICS = scarakins coordinates=xyzcab" in config_text["scara"]},
"puma": {"path": INPUTS["puma_config"], "kinematics": "pumakins", "coordinates": "XYZABC", "joints": 6, "origin": "shipped", "scan": "KINEMATICS = pumakins" in config_text["puma"]},
"scorbot": {"path": INPUTS["scorbot_config"], "kinematics": "scorbot-kins", "coordinates": "XYZAB", "joints": 5, "origin": "workspace_derived_fixture; source has no shipped sim INI", "scan": "KINEMATICS = scorbot-kins" in config_text["scorbot"]},
},
"geometry": {
"scara": {"defaults": {"D1": 490.0, "D2": 340.0, "D3": 50.0, "D4": 250.0, "D5": 50.0, "D6": 50.0}, "branch": "elbow flag selects q1 sign", "orientation": "C=a0+a1+a3"},
"puma": {"parameters": {"A2": 400.0, "A3": 50.0, "D3": 100.0, "D4": 400.0, "D6": 95.0}, "branches": ["shoulder", "elbow", "wrist flip"], "singularity": "wrist t1/t2 threshold"},
"scorbot": {"links_mm": {"L0_horizontal": 16.0, "L0_vertical": 140.0, "L1": 221.0, "L2": 221.0}, "controlled_point": "wrist J3/J4", "branch": "fixed elbow solution from source"},
},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": native_json == wasm_json},
"boundaries": {
"scara_unreachable": "SIM_KIN_ERR_RANGE verified",
"puma_unreachable_or_singular": "SIM_KIN_ERR_RANGE/SINGULAR verified",
"scorbot_unreachable": "SIM_KIN_ERR_RANGE verified",
"state_hash": "native/WASM identical after 1e-9 hash quantization",
},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/robot-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"robot_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -12,6 +12,7 @@ from __future__ import annotations
import argparse
import hashlib
import json
import re
from pathlib import Path
from typing import Iterable
@@ -29,6 +30,11 @@ BUILD_FILES = (
Path("linuxcnc/VERSION"),
Path("linuxcnc/meson.build"),
)
KINEMATICS_CONFIG_RE = re.compile(
r"(?:^|\s)KINEMATICS\s*=|\bloadrt\s+(?:\[KINS\]KINEMATICS|[^\s#;]*(?:kins|kinematics))|"
r"corexy_by_hal|matrixkins|xyzab_tdr_kins|switchkins|motion\.switchkins-type",
re.IGNORECASE,
)
def sha256(path: Path) -> str:
@@ -83,9 +89,9 @@ def make_entry(repo_root: Path, root_kind: str, path: Path) -> dict[str, object]
}
def make_manifest_entry(source: dict[str, object]) -> dict[str, object]:
def make_manifest_entry(source: dict[str, object], repo_root: Path | None = None) -> dict[str, object]:
source_id = str(source["source_id"])
return {
entry = {
"id": f"seed:{source_id}",
"feature_id": None,
"source": source_id,
@@ -108,6 +114,48 @@ def make_manifest_entry(source: dict[str, object]) -> dict[str, object]:
"exclusion_reason": None,
"mapping_status": "needs_owner",
}
source_path = str(source["path"])
is_kinematics_source = source_path.startswith("linuxcnc/src/emc/kinematics/")
is_kinematics_config = False
if repo_root is not None and source_path.startswith(("linuxcnc/configs/sim/", "linuxcnc/tests/")) and Path(source_path).suffix.lower() in {".ini", ".inc", ".hal"}:
path = repo_root / source_path
if path.is_file():
is_kinematics_config = bool(KINEMATICS_CONFIG_RE.search(path.read_text(encoding="utf-8", errors="strict")))
is_kinematics_abi = source_path in {"wasm-core/include/sim_kinematics.h", "wasm-core/src/sim_kinematics.c", "wasm-core/tests/sim_kinematics_test.c", "wasm-core/tests/sim_kinematics_test_main.c"}
is_delta_source = source_path.startswith("linuxcnc/src/emc/kinematics/lineardelta") or source_path.startswith("linuxcnc/src/emc/kinematics/rotarydelta")
is_delta_config = is_kinematics_config and any(token in source_path.lower() for token in ("ldelta", "rdelta"))
is_delta_implementation = is_kinematics_abi and source_path in {"wasm-core/include/sim_kinematics.h", "wasm-core/src/sim_kinematics.c", "wasm-core/tests/sim_kinematics_test.c", "wasm-core/tests/sim_kinematics_test_main.c"}
is_tripod_source = source_path == "linuxcnc/src/emc/kinematics/tripodkins.c"
is_tripod_config = is_kinematics_config and source_path == "linuxcnc/configs/sim/tklinuxcnc/tripod.ini"
is_genhex_source = source_path in {"linuxcnc/src/emc/kinematics/genhexkins.c", "linuxcnc/src/emc/kinematics/genhexkins.h"}
is_genhex_config = is_kinematics_config and source_path == "linuxcnc/configs/sim/axis/vismach/hexapod-sim/hexapod.ini"
is_genhex_hal = source_path == "linuxcnc/configs/sim/axis/vismach/hexapod-sim/kinematics.hal"
is_genhex_implementation = source_path in {"wasm-core/include/sim_kinematics.h", "wasm-core/src/sim_kinematics.c", "wasm-core/tests/sim_kinematics_test.c", "wasm-core/tests/sim_kinematics_test_main.c"}
is_corexy_hal = source_path in {
"linuxcnc/src/hal/components/corexy_by_hal.comp",
"linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini",
"linuxcnc/configs/sim/axis/corexy/corexy_by_hal.hal",
"linuxcnc/configs/sim/axis/corexy/corexy_by_kins.ini",
"wasm-core/include/sim_hal.h",
"wasm-core/src/sim_hal.c",
"wasm-core/tests/sim_hal_test.c",
"wasm-core/tests/sim_hal_test_main.c",
}
if is_kinematics_source or is_kinematics_config or is_kinematics_abi or is_corexy_hal:
# K0-INV-01 audits are source-closure evidence, not an implementation
# claim. Keep the seed planned while linking every kinematics source
# to the deterministic report and its explicit blocked oracle.
entry.update({
"domain": "hal" if is_corexy_hal else "kinematics",
"feature_id": "feature:kinematics-k2-hal" if is_corexy_hal else "feature:kinematics-k4-tripod" if (is_tripod_source or is_tripod_config) else "feature:kinematics-k4-genhex" if (is_genhex_source or is_genhex_config or is_genhex_hal or is_genhex_implementation) else "feature:kinematics-k4-delta" if (is_delta_source or is_delta_config or is_delta_implementation) else "feature:kinematics-k0" if not is_kinematics_abi else "feature:kinematics-k1-contract",
"dependencies": ["coverage/coupled-kinematics.report.json", "coverage/config-resources.jsonl"] if is_corexy_hal else ["coverage/tripod-kinematics.report.json", "coverage/delta-kinematics.report.json"] if (is_tripod_source or is_tripod_config) else ["coverage/genhex-kinematics.report.json", "coverage/kinematics-source-closure.report.json"] if (is_genhex_source or is_genhex_config or is_genhex_hal or is_genhex_implementation) else ["coverage/delta-kinematics.report.json", "coverage/kinematics-source-closure.report.json"] if (is_delta_source or is_delta_config or is_delta_implementation) else ["coverage/source-index.jsonl", "coverage/source-symbols.jsonl"] if is_kinematics_source else ["coverage/kinematics-source-closure.report.json", "coverage/config-resources.jsonl"] if is_kinematics_config else ["wasm-core/include/sim_kinematics.h", "wasm-core/CMakeLists.txt"],
"outputs": ["coverage/corexy-hal.report.json"] if is_corexy_hal else ["coverage/tripod-kinematics.report.json"] if (is_tripod_source or is_tripod_config) else ["coverage/genhex-kinematics.report.json"] if (is_genhex_source or is_genhex_config or is_genhex_hal or is_genhex_implementation) else ["coverage/delta-kinematics.report.json"] if (is_delta_source or is_delta_config or is_delta_implementation) else ["coverage/kinematics-source-closure.report.json", "coverage/trivkins-integration.report.json", "coverage/coupled-kinematics.report.json", "coverage/fiveaxis-kinematics.report.json", "coverage/trt-kinematics.report.json", "coverage/robot-kinematics.report.json", "coverage/genser-kinematics.report.json"] if is_kinematics_source else ["coverage/kinematics-config-graph.report.json", "coverage/kinematics-oracle-samples.jsonl", "coverage/kinematics-oracle.report.json", "coverage/kinematics-golden-schema.json", "coverage/coupled-kinematics.report.json", "coverage/fiveaxis-kinematics.report.json", "coverage/trt-kinematics.report.json", "coverage/robot-kinematics.report.json", "coverage/genser-kinematics.report.json"] if is_kinematics_config else ["coverage/kinematics-abi-contract.json", "coverage/kinematics-mapping-contract.json", "coverage/kinematics-numeric-policy.json", "coverage/coupled-kinematics.report.json", "coverage/fiveaxis-kinematics.report.json", "coverage/trt-kinematics.report.json", "coverage/robot-kinematics.report.json", "coverage/genser-kinematics.report.json"],
"errors": ["blocked:linuxcnc/unit_tests/kinematics missing from locked source"],
"test_id": "audit:K2-HAL-01" if is_corexy_hal else "audit:K4-TRIPOD-01" if (is_tripod_source or is_tripod_config) else "audit:K4-HEX-01" if (is_genhex_source or is_genhex_config or is_genhex_hal or is_genhex_implementation) else "audit:K4-DELTA-01" if (is_delta_source or is_delta_config or is_delta_implementation) else "audit:K0-INV-01" if is_kinematics_source else "audit:K0-INV-02" if is_kinematics_config else "audit:K1-CONTRACT-01",
"oracle": {"status": "blocked", "report": "coverage/corexy-hal.report.json" if is_corexy_hal else "coverage/tripod-kinematics.report.json" if (is_tripod_source or is_tripod_config) else "coverage/genhex-kinematics.report.json" if (is_genhex_source or is_genhex_config or is_genhex_hal or is_genhex_implementation) else "coverage/delta-kinematics.report.json" if (is_delta_source or is_delta_config or is_delta_implementation) else "coverage/kinematics-source-closure.report.json"},
"mapping_status": "audited_blocked" if not is_corexy_hal else "verified_partial",
})
return entry
def write_jsonl(path: Path, entries: Iterable[dict[str, object]]) -> None:
@@ -122,7 +170,7 @@ def generate(repo_root: Path, source_index: Path, manifest: Path) -> tuple[int,
sources = [make_entry(repo_root, kind, path) for kind, path in iter_files(repo_root)]
sources.sort(key=lambda item: str(item["source_id"]))
write_jsonl(source_index, sources)
write_jsonl(manifest, (make_manifest_entry(source) for source in sources))
write_jsonl(manifest, (make_manifest_entry(source, repo_root) for source in sources))
return len(sources), len(sources)

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Generate deterministic native/WASM evidence for tripod kinematics."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import subprocess
from pathlib import Path
INPUTS = {
"source": "linuxcnc/src/emc/kinematics/tripodkins.c",
"config": "linuxcnc/configs/sim/tklinuxcnc/tripod.ini",
"hal": "linuxcnc/lib/hallib/tripodsim.hal",
"implementation": "wasm-core/src/sim_kinematics.c",
"header": "wasm-core/include/sim_kinematics.h",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_json(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.lstrip().startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit JSON")
def hal_geometry(text: str) -> dict[str, float]:
values: dict[str, float] = {}
for name, value in re.findall(r"^setp\s+tripodkins\.(Bx|Cx|Cy)\s+([-+0-9.eE]+)\s*$", text, re.M):
values[name] = float(value)
return values
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_json([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_json(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = {"tripod": native_all["tripod"], "boundaries": native_all["delta_edges"]["tripod"]}
wasm = {"tripod": wasm_all["tripod"], "boundaries": wasm_all["delta_edges"]["tripod"]}
native_text = json.dumps(native, sort_keys=True, separators=(",", ":"))
wasm_text = json.dumps(wasm, sort_keys=True, separators=(",", ":"))
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
config_text = (root / INPUTS["config"]).read_text(encoding="utf-8")
hal_text = (root / INPUTS["hal"]).read_text(encoding="utf-8")
edges = native["boundaries"]
report = {
"schema": "tripod-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidate": {
"path": INPUTS["config"],
"kinematics": "tripodkins",
"joints": 3,
"coordinates": "XYZ",
"scan": "KINEMATICS = tripodkins" in config_text and "JOINTS = 3" in config_text,
"hal_library": INPUTS["hal"],
},
"geometry": {"base_A": [0.0, 0.0, 0.0], "base_B": [10.0, 0.0, 0.0], "base_C": [5.0, 7.071, 0.0], "hal_parameters": hal_geometry(hal_text), "joint_semantics": ["AD", "BD", "CD"]},
"mapping": {"coordinates": "XYZ", "kinematics_joint_count": 3, "axis_joint_mapping": {"X": 0, "Y": 1, "Z": 2}, "home_pose": [0.0, 0.0, 1.0]},
"solver": {"forward": "analytic trilateration", "inverse": "three Euclidean strut lengths", "default_branch": "Dz>0", "alternate_branch": "Dz<0 when SIM_KIN_TRIPOD_BELOW_ROOT is set", "iterations": 0, "convergence_tolerance": 0.0},
"workspace_and_boundaries": {"nominal": native["tripod"], "alternate_pose": edges["alternate_pose"], "root_branch": edges["branch"], "alternate_branch": edges["alternate_branch"], "no_solution_error": edges["no_solution"], "base_plane_singular_error": edges["singular"], "joint_limit_error": edges["limit_error"], "geometry_error": edges["geometry_error"], "iterations": edges["iterations"]},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": native_text == wasm_text, "state_hash_identical": native["tripod"]["state_hash"] == wasm["tripod"]["state_hash"]},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source", "blocked_samples": ["nominal above-root", "alternate below-root", "triangle-inequality no-solution", "base-plane singular", "joint-limit", "degenerate base geometry"]},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/tripod-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"tripod_kinematics={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Generate evidence for the currently implemented trivkins integration slice."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
closure = json.loads((root / "coverage/kinematics-source-closure.report.json").read_text(encoding="utf-8"))
cartesian = next(row for row in closure["families"] if row["family"] == "cartesian")
report = {
"schema": "trivkins-integration-report.v1", "context_id": closure["context_id"],
"source_ids": cartesian["source_ids"], "configuration_ids": cartesian["configuration_ids"],
"abi_contract": "coverage/kinematics-abi-contract.json", "mapping_contract": "coverage/kinematics-mapping-contract.json", "numeric_policy": "coverage/kinematics-numeric-policy.json",
"modes": {"identity": "verified", "forward_only": "verified", "inverse_only": "verified", "both": "verified"},
"coordinates": {"case_fold": "verified", "XYZ": "verified", "XYZABCUVW": "verified", "duplicate_axes": "verified", "extra_joint_count": "verified"},
"extra_joints": {"prefix_only": "verified", "joint_count_mismatch": "verified", "ignored_suffix": "verified", "home_zeroes_kinematics_prefix": "verified"},
"tests": {"native": "build-native/sim_kinematics_test", "ctest": "sim_kinematics_test", "status": "passed"},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source", "configuration_candidates": cartesian["configuration_ids"][:16]},
"status": "partial",
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/trivkins-integration.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"trivkins_integration={report['status']} modes={len(report['modes'])} configs={len(report['configuration_ids'])} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""Generate deterministic evidence for xyzac/xyzbc TRT kinematics."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
INPUTS = {
"xyzac_source": "linuxcnc/src/emc/kinematics/xyzac-trt-kins.c",
"xyzbc_source": "linuxcnc/src/emc/kinematics/xyzbc-trt-kins.c",
"trt_source": "linuxcnc/src/emc/kinematics/trtfuncs.c",
"xyzac_config": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
"xyzbc_config": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini",
"implementation": "wasm-core/src/sim_kinematics.c",
"test": "wasm-core/tests/sim_kinematics_test.c",
}
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def run_sample(command: list[str], root: Path) -> dict[str, object]:
completed = subprocess.run(command, cwd=root, check=False, text=True, capture_output=True)
if completed.returncode != 0:
raise RuntimeError(f"{command[0]} exited {completed.returncode}: {completed.stderr.strip()}")
for line in reversed(completed.stdout.splitlines()):
if line.startswith("{"):
return json.loads(line)
raise RuntimeError(f"{command[0]} did not emit a TRT sample")
def generate(root: Path, output: Path) -> dict[str, object]:
root = root.resolve()
native_all = run_sample([str(root / "build-native/sim_kinematics_test")], root)
wasm_all = run_sample(["node", str(root / "build-wasm/sim_kinematics_wasm_test.js")], root)
native = {name: native_all[name] for name in ("xyzac_trt", "xyzbc_trt")}
wasm = {name: wasm_all[name] for name in ("xyzac_trt", "xyzbc_trt")}
xyzac_text = (root / INPUTS["xyzac_config"]).read_text(encoding="utf-8")
xyzbc_text = (root / INPUTS["xyzbc_config"]).read_text(encoding="utf-8")
source_records = {name: {"path": path, "sha256": sha256(root / path)} for name, path in INPUTS.items()}
native_json = json.dumps(native, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
wasm_json = json.dumps(wasm, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
report = {
"schema": "trt-kinematics-report.v1",
"status": "partial",
"implementation_status": "verified",
"source_records": source_records,
"configuration_candidates": {
"xyzac": {"path": INPUTS["xyzac_config"], "kinematics": "xyzac-trt-kins", "sparm": "identityfirst", "joints": 5, "scan": "xyzac-trt-kins sparm=identityfirst" in xyzac_text},
"xyzbc": {"path": INPUTS["xyzbc_config"], "kinematics": "xyzbc-trt-kins", "sparm": "identityfirst", "joints": 5, "scan": "xyzbc-trt-kins sparm=identityfirst" in xyzbc_text},
},
"rotation_chain": {
"xyzac": "C about Z then A about X with y/z/tool offsets",
"xyzbc": "C about Z then B about Y with x/z/tool offsets",
"direction": "LinuxCNC opposite conventional rotary directions retained",
"identityfirst": "switchkins type 0 identity, type 1 TRT, type 2 userk",
},
"offsets": {
"xyzac": {"x_rot_point": 20.0, "y_offset": 3.0, "tool_offset": 100.0},
"xyzbc": {"x_offset": 5.0, "z_offset": 2.0, "tool_offset": 50.0},
},
"samples": {"native": native, "wasm": wasm, "byte_identical_json": native_json == wasm_json},
"boundaries": {
"xyzac_round_trip": "verified",
"xyzbc_round_trip": "verified",
"xyzbc_b_90_singular": "SIM_KIN_ERR_SINGULAR verified",
"missing_required_axis": "SIM_KIN_ERR_CONFIGURATION verified by existing contract tests",
"state_hash": "native/WASM identical",
},
"oracle": {"status": "blocked", "reason": "linuxcnc/unit_tests/kinematics is absent from the locked source"},
"native_wasm": {"status": "passed", "native_command": "build-native/sim_kinematics_test", "wasm_command": "node build-wasm/sim_kinematics_wasm_test.js"},
}
report["report_sha256"] = hashlib.sha256(json.dumps(report, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
return report
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output", type=Path, default=Path("coverage/trt-kinematics.report.json"))
args = parser.parse_args()
report = generate(args.repo_root, args.repo_root / args.output)
print(f"trt={report['status']} native_wasm={report['native_wasm']['status']} oracle={report['oracle']['status']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

426
scripts/model_stream.py Normal file
View File

@@ -0,0 +1,426 @@
#!/usr/bin/env python3
"""Consume bounded model SSE streams without reusing partial responses.
The module is intentionally provider-neutral. It understands the event
envelope used by the Responses API, but does not perform network I/O. A
caller passes the byte chunks it receives and gets a terminal result plus an
atomic checkpoint. Partial model text is counted for diagnostics and is
never written to the checkpoint or returned as reusable context.
"""
from __future__ import annotations
import codecs
import hashlib
import json
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Iterable
from validate_model_budget import ModelBudgetError, validate_provider_response
DEFAULT_MAX_EVENT_BYTES = 256 * 1024
DEFAULT_MAX_RESPONSE_BYTES = 8 * 1024 * 1024
MAX_ERROR_TEXT = 1024
PROFILE_RETRY = {
"milestone": "contract",
"contract": "feature",
"feature": "doc",
"doc": "doc",
}
class StreamProtocolError(ValueError):
"""Raised when a stream cannot be decoded as bounded SSE."""
@dataclass(frozen=True)
class SSEEvent:
event: str
data: str
event_id: str | None = None
class SSEDecoder:
"""Incrementally decode UTF-8 SSE frames with hard size limits."""
def __init__(
self,
*,
max_event_bytes: int = DEFAULT_MAX_EVENT_BYTES,
max_response_bytes: int = DEFAULT_MAX_RESPONSE_BYTES,
) -> None:
if max_event_bytes <= 0 or max_response_bytes <= 0:
raise ValueError("stream byte limits must be positive")
if max_event_bytes > max_response_bytes:
raise ValueError("event limit cannot exceed response limit")
self.max_event_bytes = int(max_event_bytes)
self.max_response_bytes = int(max_response_bytes)
self.bytes_received = 0
self._decoder = codecs.getincrementaldecoder("utf-8")("strict")
self._line_buffer = ""
self._data: list[str] = []
self._event = "message"
self._event_id: str | None = None
self._event_bytes = 0
def _dispatch(self) -> SSEEvent | None:
if not self._data:
self._event = "message"
self._event_id = None
self._event_bytes = 0
return None
event = SSEEvent(self._event, "\n".join(self._data), self._event_id)
self._data = []
self._event = "message"
self._event_id = None
self._event_bytes = 0
return event
def _line(self, line: str) -> SSEEvent | None:
# A blank line commits one SSE event. Ignore comments and unknown
# fields, as required by the SSE framing rules.
if line == "":
return self._dispatch()
self._event_bytes += len(line.encode("utf-8")) + 1
if self._event_bytes > self.max_event_bytes:
raise StreamProtocolError("SSE event exceeds byte budget")
if line.startswith(":"):
return None
field, separator, value = line.partition(":")
if separator and value.startswith(" "):
value = value[1:]
if field == "event":
self._event = value or "message"
elif field == "data":
self._data.append(value)
elif field == "id":
self._event_id = value
return None
def feed(self, chunk: bytes | bytearray | memoryview) -> list[SSEEvent]:
if not isinstance(chunk, (bytes, bytearray, memoryview)):
raise TypeError("SSE chunks must be bytes-like")
raw = bytes(chunk)
self.bytes_received += len(raw)
if self.bytes_received > self.max_response_bytes:
raise StreamProtocolError("SSE response exceeds byte budget")
try:
self._line_buffer += self._decoder.decode(raw, final=False)
except UnicodeDecodeError as exc:
raise StreamProtocolError("SSE response is not valid UTF-8") from exc
events: list[SSEEvent] = []
while True:
newline = self._line_buffer.find("\n")
if newline < 0:
if len(self._line_buffer.encode("utf-8")) > self.max_event_bytes:
raise StreamProtocolError("SSE line exceeds byte budget")
break
line = self._line_buffer[:newline]
self._line_buffer = self._line_buffer[newline + 1 :]
if line.endswith("\r"):
line = line[:-1]
event = self._line(line)
if event is not None:
events.append(event)
return events
def finish(self) -> list[SSEEvent]:
try:
self._line_buffer += self._decoder.decode(b"", final=True)
except UnicodeDecodeError as exc:
raise StreamProtocolError("SSE response ends with incomplete UTF-8") from exc
events: list[SSEEvent] = []
if self._line_buffer:
line = self._line_buffer
self._line_buffer = ""
event = self._line(line.rstrip("\r"))
if event is not None:
events.append(event)
event = self._dispatch()
if event is not None:
events.append(event)
return events
def _json_event(event: SSEEvent) -> dict[str, Any]:
try:
value = json.loads(event.data)
except (TypeError, json.JSONDecodeError) as exc:
raise StreamProtocolError("SSE data is not a JSON object") from exc
if not isinstance(value, dict):
raise StreamProtocolError("SSE data must be a JSON object")
return value
def _event_type(event: SSEEvent, payload: dict[str, Any]) -> str:
value = payload.get("type")
return str(value) if value is not None else event.event
def _nested_response(payload: dict[str, Any]) -> dict[str, Any]:
response = payload.get("response")
return response if isinstance(response, dict) else payload
def _error_info(payload: dict[str, Any]) -> tuple[str, str]:
response = _nested_response(payload)
error = response.get("error")
if isinstance(error, dict):
code = str(error.get("code", "provider_error"))
message = str(error.get("message", "provider response failed"))
else:
code = str(response.get("code", "provider_error"))
message = str(response.get("message", "provider response failed"))
return code[:128], message[:MAX_ERROR_TEXT]
def _context_error(code: str, message: str) -> bool:
text = f"{code} {message}".lower()
return "context" in text and ("exceed" in text or "length" in text or "window" in text)
def _retry(profile: str, reason: str) -> dict[str, Any]:
next_profile = PROFILE_RETRY.get(profile, "doc")
context_overflow = "context" in reason.lower() and (
"exceed" in reason.lower() or "length" in reason.lower()
)
return {
"action": "split_task" if profile == "doc" and context_overflow else "rebuild_packet",
"profile": next_profile,
"reuse_packet": False,
"drop_optional_evidence": True,
"reason": reason,
}
def _checkpoint(path: Path | None, value: dict[str, Any]) -> str | None:
if path is None:
return None
path.parent.mkdir(parents=True, exist_ok=True)
payload = json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
temporary = path.with_name(f".{path.name}.tmp")
try:
with temporary.open("wb") as handle:
handle.write(payload)
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
finally:
try:
temporary.unlink()
except FileNotFoundError:
pass
return str(path)
def _result(
*,
context_id: str,
packet_id: str,
profile: str,
status: str,
decoder: SSEDecoder,
event_count: int,
output_bytes: int,
output_digest: Any,
response_id: str | None,
last_event_id: str | None,
reason_code: str | None = None,
error: str | None = None,
usage: dict[str, int] | None = None,
finish_reason: str | None = None,
checkpoint_path: Path | None = None,
) -> dict[str, Any]:
reusable = status == "completed"
result: dict[str, Any] = {
"schema": "model-stream-result.v1",
"context_id": context_id,
"packet_id": packet_id,
"status": status,
"reusable": reusable,
"event_count": event_count,
"bytes_received": decoder.bytes_received,
"output_bytes": output_bytes,
"output_sha256": output_digest.hexdigest(),
"response_id": response_id,
"last_event_id": last_event_id,
"reason_code": reason_code,
"error": error,
"finish_reason": finish_reason,
"retry": None if reusable else _retry(profile, reason_code or status),
}
if usage is not None:
result["usage"] = usage
checkpoint = {
"schema": "model-stream-checkpoint.v1",
"context_id": context_id,
"packet_id": packet_id,
"status": status,
"reusable": reusable,
"reason_code": reason_code,
"event_count": event_count,
"bytes_received": decoder.bytes_received,
"output_bytes": output_bytes,
"output_sha256": output_digest.hexdigest(),
"response_id": response_id,
"last_event_id": last_event_id,
"finish_reason": finish_reason,
"retry": result["retry"],
}
if usage is not None:
checkpoint["usage"] = usage
result["checkpoint_path"] = _checkpoint(checkpoint_path, checkpoint)
return result
def consume_sse(
chunks: Iterable[bytes],
*,
context_id: str,
packet_id: str,
profile: str = "feature",
max_output_tokens: int = 1024,
max_output_bytes: int = DEFAULT_MAX_RESPONSE_BYTES,
checkpoint_path: Path | None = None,
max_event_bytes: int = DEFAULT_MAX_EVENT_BYTES,
max_response_bytes: int = DEFAULT_MAX_RESPONSE_BYTES,
) -> dict[str, Any]:
"""Consume a stream and return a terminal, checkpointed result.
Any disconnect, malformed frame, provider failure, context overflow, or
output overrun is non-reusable. The returned retry plan explicitly asks
the caller to rebuild a smaller packet; no partial output is included.
"""
decoder = SSEDecoder(
max_event_bytes=max_event_bytes,
max_response_bytes=max_response_bytes,
)
if max_output_tokens < 0 or max_output_bytes <= 0:
raise ValueError("output limits must be non-negative/positive")
event_count = 0
output_bytes = 0
output_digest = hashlib.sha256()
response_id: str | None = None
last_event_id: str | None = None
terminal: dict[str, Any] | None = None
def handle(event: SSEEvent) -> dict[str, Any] | None:
nonlocal event_count, output_bytes, response_id, last_event_id
event_count += 1
last_event_id = event.event_id or last_event_id
payload = _json_event(event)
kind = _event_type(event, payload)
response = _nested_response(payload)
if response.get("id") is not None:
response_id = str(response["id"])
delta = payload.get("delta")
if isinstance(delta, str):
encoded = delta.encode("utf-8")
output_bytes += len(encoded)
output_digest.update(encoded)
if output_bytes > max_output_bytes:
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="output_budget_exceeded", decoder=decoder,
event_count=event_count, output_bytes=output_bytes,
output_digest=output_digest, response_id=response_id,
last_event_id=last_event_id, reason_code="output_overrun",
checkpoint_path=checkpoint_path,
)
if kind in {"response.failed", "error"}:
code, message = _error_info(payload)
status = "context_budget_exceeded" if _context_error(code, message) else "provider_failed"
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status=status, decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code=code, error=message, checkpoint_path=checkpoint_path,
)
if kind in {"response.completed", "response.incomplete"}:
try:
validated = validate_provider_response(
payload, max_output_tokens=max_output_tokens
)
except ModelBudgetError as exc:
message = str(exc)
overrun = "exceeded output token budget" in message
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="output_budget_exceeded" if overrun else "protocol_error",
decoder=decoder,
event_count=event_count, output_bytes=output_bytes,
output_digest=output_digest, response_id=response_id,
last_event_id=last_event_id,
reason_code="output_overrun" if overrun else "invalid_usage",
error=message, checkpoint_path=checkpoint_path,
)
status = "truncated" if bool(validated.get("truncated")) else "completed"
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status=status, decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code="length" if status == "truncated" else None,
usage={key: int(validated[key]) for key in ("input_tokens", "output_tokens", "total_tokens")},
finish_reason=validated.get("finish_reason"),
checkpoint_path=checkpoint_path,
)
return None
try:
for chunk in chunks:
for event in decoder.feed(chunk):
terminal = handle(event)
if terminal is not None:
return terminal
for event in decoder.finish():
terminal = handle(event)
if terminal is not None:
return terminal
except (ConnectionError, TimeoutError, OSError) as exc:
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="interrupted", decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code="stream_disconnected", error=type(exc).__name__,
checkpoint_path=checkpoint_path,
)
except StreamProtocolError as exc:
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="protocol_error", decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code="stream_decode_error", error=str(exc),
checkpoint_path=checkpoint_path,
)
except Exception as exc:
# SDKs use different exception classes for a body that ends before
# ``response.completed``. Treat all ordinary iterator exceptions as
# a disconnect, but keep only the class name in the checkpoint.
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="interrupted", decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code="stream_disconnected", error=type(exc).__name__,
checkpoint_path=checkpoint_path,
)
return _result(
context_id=context_id, packet_id=packet_id, profile=profile,
status="interrupted", decoder=decoder, event_count=event_count,
output_bytes=output_bytes, output_digest=output_digest,
response_id=response_id, last_event_id=last_event_id,
reason_code="stream_ended_without_completion",
checkpoint_path=checkpoint_path,
)
__all__ = ["SSEDecoder", "SSEEvent", "StreamProtocolError", "consume_sse"]

View File

@@ -6,6 +6,7 @@ from pathlib import Path
from tempfile import TemporaryDirectory
from context_packet import PacketError, generate_packet
from validate_model_budget import validate_request
class ContextPacketTests(unittest.TestCase):
@@ -115,6 +116,67 @@ class ContextPacketTests(unittest.TestCase):
self.assertEqual([item["path"] for item in packet["evidence"]["selected"]], ["required.log", "a.log"])
self.assertEqual(packet["evidence"]["excluded"][0]["reason_code"], "E3")
def test_token_budget_is_enforced_during_generation(self):
packet = generate_packet(
self.base([{"path": "large.log", "size": 1800, "priority": 1}]),
context_id="c1",
packet_id="p1",
profile="doc",
total_bytes=8192,
evidence_bytes=2048,
)
self.assertEqual(packet["evidence"]["selected"], [])
self.assertEqual(packet["evidence"]["excluded"][0]["reason_code"], "E6")
def test_required_item_fails_on_token_budget(self):
with self.assertRaisesRegex(PacketError, r"required evidence cannot fit \(E6\)"):
generate_packet(
self.base([{"path": "required.log", "size": 1800, "required": True}]),
context_id="c1",
packet_id="p1",
profile="doc",
total_bytes=8192,
evidence_bytes=2048,
)
def test_overrides_cannot_expand_profile(self):
with self.assertRaisesRegex(PacketError, "bytes budget override exceeds profile"):
generate_packet(
self.base([]),
context_id="c1",
packet_id="p1",
profile="doc",
total_bytes=8193,
)
def test_token_limit_override_can_only_shrink_profile(self):
packet = generate_packet(
self.base([]),
context_id="c1",
packet_id="p1",
profile="feature",
token_limit=512,
)
self.assertEqual(packet["budget"]["token_limit"], 512)
with self.assertRaisesRegex(PacketError, "tokens budget override exceeds profile"):
generate_packet(
self.base([]),
context_id="c1",
packet_id="p1",
profile="feature",
token_limit=4097,
)
def test_generated_packet_passes_model_preflight(self):
packet = generate_packet(
self.base([]),
context_id="c1",
packet_id="p1",
profile="feature",
)
result = validate_request(packet, context_window=16384, overhead_tokens=256)
self.assertLessEqual(result["reserved_total_tokens"], 16384)
def test_cli_does_not_replace_existing_output_on_generation_failure(self):
with TemporaryDirectory() as directory:
root = Path(directory)

View File

@@ -0,0 +1,36 @@
import json
import unittest
from pathlib import Path
import generate_corexy_hal_report
ROOT = Path(__file__).resolve().parents[1]
class CorexyHalReportTests(unittest.TestCase):
def test_report_contract(self):
report = json.loads((ROOT / "coverage/corexy-hal.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "corexy-hal-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertEqual(report["native_wasm"]["status"], "passed")
self.assertTrue(report["native_wasm"]["byte_identical_json"])
self.assertEqual(report["home_semantics"]["status"], "verified")
self.assertEqual(report["oracle"]["status"], "blocked")
self.assertTrue(all(report["config_scan"].values()))
def test_report_is_deterministic(self):
first = ROOT / "coverage/.corexy-hal-one.json"
second = ROOT / "coverage/.corexy-hal-two.json"
try:
self.assertEqual(
generate_corexy_hal_report.generate(ROOT, first),
generate_corexy_hal_report.generate(ROOT, second),
)
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,38 @@
import json
import unittest
from pathlib import Path
import generate_coupled_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class CoupledKinematicsReportTests(unittest.TestCase):
def test_report_contract(self):
report = json.loads((ROOT / "coverage/coupled-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "coupled-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertEqual(report["native_wasm"]["status"], "passed")
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertTrue(report["corexy_entry_distinction"]["distinct"])
self.assertEqual(report["closure"]["official_oracle"], "blocked")
self.assertEqual(set(report["families"]), {"corexy", "rose", "maxkins", "rotatekins"})
self.assertIn("rotatekins", report["configuration_candidates"])
self.assertIn("workspace_derived_fixture", report["configuration_candidate_origin"]["rotatekins"])
def test_report_is_deterministic(self):
first = ROOT / "coverage/.coupled-one.json"
second = ROOT / "coverage/.coupled-two.json"
try:
self.assertEqual(
generate_coupled_kinematics_report.generate(ROOT, first),
generate_coupled_kinematics_report.generate(ROOT, second),
)
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,38 @@
import json
import unittest
from pathlib import Path
import generate_delta_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class DeltaKinematicsReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/delta-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "delta-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertEqual(report["native_wasm"]["status"], "passed")
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertTrue(report["samples"]["state_hash_identical"])
self.assertEqual(report["oracle"]["status"], "blocked")
self.assertEqual(report["configuration_candidate_count"], 4)
self.assertIn("linear_cc", report["source_records"])
self.assertIn("rotary_cc", report["source_records"])
self.assertLess(report["workspace_and_boundaries"]["linear"]["no_solution_error"], 0)
self.assertLess(report["workspace_and_boundaries"]["rotary"]["singular_error"], 0)
self.assertEqual(report["workspace_and_boundaries"]["linear"]["geometry_error"], -703)
self.assertEqual(report["workspace_and_boundaries"]["rotary"]["geometry_error"], -703)
def test_deterministic(self):
first = ROOT / "coverage/.delta-one.json"
second = ROOT / "coverage/.delta-two.json"
try:
self.assertEqual(generate_delta_kinematics_report.generate(ROOT, first), generate_delta_kinematics_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,32 @@
import json
import unittest
from pathlib import Path
import generate_fiveaxis_report
ROOT = Path(__file__).resolve().parents[1]
class FiveaxisReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/fiveaxis-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "fiveaxis-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertTrue(report["configuration"]["scan"]["kins_entry"])
self.assertTrue(report["configuration"]["scan"]["joint_count"])
self.assertTrue(report["sample"]["byte_identical_json"])
self.assertEqual(report["oracle"]["status"], "blocked")
def test_deterministic(self):
first = ROOT / "coverage/.fiveaxis-one.json"
second = ROOT / "coverage/.fiveaxis-two.json"
try:
self.assertEqual(generate_fiveaxis_report.generate(ROOT, first), generate_fiveaxis_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,29 @@
import hashlib
import json
import tempfile
import unittest
from pathlib import Path
try:
import generate_posemath_report
except ModuleNotFoundError: # Supports both discovery and direct module execution.
from scripts import generate_posemath_report
class PosemathReportTests(unittest.TestCase):
def test_report_is_deterministic(self):
root = Path(__file__).resolve().parents[1]
with tempfile.TemporaryDirectory() as directory:
report_path = Path(directory) / "report.json"
first = generate_posemath_report.generate(root, report_path)
first_bytes = report_path.read_bytes()
second = generate_posemath_report.generate(root, report_path)
self.assertEqual(first, second)
self.assertEqual(first_bytes, report_path.read_bytes())
self.assertEqual(hashlib.sha256(first_bytes).hexdigest(), hashlib.sha256(report_path.read_bytes()).hexdigest())
self.assertGreater(first["inputs"]["file_count"], 0)
self.assertEqual(first["outputs"]["file_count"], len(generate_posemath_report.OUTPUTS))
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,32 @@
import json
import unittest
from pathlib import Path
import generate_genhex_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class GenhexKinematicsReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/genhex-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "genhex-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertTrue(report["configuration_candidate"]["scan"])
self.assertEqual(report["geometry"]["struts"], 6)
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertTrue(report["samples"]["state_hash_identical"])
self.assertEqual(report["oracle"]["status"], "blocked")
def test_deterministic(self):
first = ROOT / "coverage/.genhex-one.json"
second = ROOT / "coverage/.genhex-two.json"
try:
self.assertEqual(generate_genhex_kinematics_report.generate(ROOT, first), generate_genhex_kinematics_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,28 @@
import json
import unittest
from pathlib import Path
import generate_genser_report
ROOT = Path(__file__).resolve().parents[1]
class GenserReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/genser-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "genser-kinematics-report.v1")
self.assertTrue(all(row["scan"] for row in report["configuration_candidates"].values()))
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertGreaterEqual(report["solver"]["iterations"], 0)
self.assertEqual(report["oracle"]["status"], "blocked")
def test_deterministic(self):
first = ROOT / "coverage/.genser-one.json"
second = ROOT / "coverage/.genser-two.json"
try:
self.assertEqual(generate_genser_report.generate(ROOT, first), generate_genser_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import json
import json
import subprocess
import unittest
from pathlib import Path
@@ -17,7 +18,7 @@ class HalRuntimeTests(unittest.TestCase):
def test_header_declares_error_boundary(self):
header = (ROOT / "wasm-core/include/sim_hal.h").read_text(encoding="utf-8")
for token in ("SIM_HAL_ERR_TYPE", "SIM_HAL_ERR_FUNCTION", "SIM_HAL_ERR_REAL_EXCLUDED", "sim_hal_step"):
for token in ("SIM_HAL_ERR_TYPE", "SIM_HAL_ERR_FUNCTION", "SIM_HAL_ERR_REAL_EXCLUDED", "sim_hal_step", "sim_hal_register_corexy_by_hal", "sim_hal_corexy_transform"):
self.assertIn(token, header)
def test_wasm_exports_hal_api(self):
@@ -28,6 +29,11 @@ class HalRuntimeTests(unittest.TestCase):
for symbol in ("sim_hal_register_pin", "sim_hal_register_thread", "sim_hal_step"):
self.assertIn(symbol, text)
def test_corexy_hal_report(self):
report = json.loads((ROOT / "coverage/corexy-hal.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "corexy-hal-report.v1")
self.assertTrue(report["native_wasm"]["byte_identical_json"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,37 @@
import unittest
from pathlib import Path
import generate_interpreter_report
ROOT = Path(__file__).resolve().parents[1]
class InterpreterRuntimeTests(unittest.TestCase):
def test_native_interpreter_binary(self):
binary = ROOT / "build-native/sim_interpreter_test"
if not binary.is_file():
self.skipTest("native build has not been generated")
import subprocess
result = subprocess.run([str(binary)], cwd=ROOT, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.assertEqual(result.returncode, 0, result.stderr.decode(errors="replace"))
def test_wasm_exports_interpreter_api(self):
module = ROOT / "build-wasm/linuxcnc-sim-core.js"
if not module.is_file():
self.skipTest("WASM build has not been generated")
text = module.read_text(encoding="utf-8")
for symbol in ("sim_interpreter_init", "sim_interpreter_execute", "sim_interpreter_canonical", "sim_interpreter_state_hash"):
self.assertIn(symbol, text)
def test_report_is_deterministic_and_complete(self):
report = __import__("json").loads((ROOT / "coverage/interpreter-runtime.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "interpreter-runtime-report.v1")
self.assertGreater(report["inputs"]["file_count"], 0)
self.assertEqual(report["outputs"]["file_count"], len(generate_interpreter_report.OUTPUTS))
self.assertTrue(report["canonical"]["byte_identical"])
self.assertTrue(report["canonical"]["state_hash_identical"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,40 @@
import json
import sys
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
sys.path.insert(0, str(Path(__file__).resolve().parent))
from generate_kinematics_abi_contract import generate
class KinematicsAbiContractTests(unittest.TestCase):
def test_repository_contract_is_verified(self):
root = Path(__file__).resolve().parents[1]
report = json.loads((root / "coverage/kinematics-abi-contract.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "kinematics-abi-contract.v1")
self.assertEqual(report["status"], "verified")
self.assertEqual(report["missing_required_exports"], [])
self.assertEqual(report["errors"]["SIM_KIN_ERR_NONFINITE"], -701)
def test_missing_export_blocks_contract(self):
directory = TemporaryDirectory()
self.addCleanup(directory.cleanup)
root = Path(directory.name)
(root / "wasm-core/include").mkdir(parents=True)
(root / "wasm-core/include/sim_kinematics.h").write_text(
"enum sim_kinematics_result { SIM_KIN_OK = 0 };\n"
"typedef struct sim_kinematics_config { int type; } sim_kinematics_config;\n"
"int sim_kinematics_init(void);\nint sim_kinematics_forward(void);\n",
encoding="utf-8",
)
(root / "wasm-core/CMakeLists.txt").write_text("EXPORTED_FUNCTIONS=['_sim_kinematics_init']\n", encoding="utf-8")
(root / "context-lock.json").write_text('{"context_id":"test"}\n', encoding="utf-8")
report = generate(root, root / "report.json")
self.assertEqual(report["status"], "blocked")
self.assertIn("sim_kinematics_state_hash", report["missing_required_exports"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,60 @@
import json
import sys
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
sys.path.insert(0, str(Path(__file__).resolve().parent))
from generate_kinematics_closure_report import generate
class KinematicsClosureReportTests(unittest.TestCase):
def make_tree(self):
directory = TemporaryDirectory()
root = Path(directory.name)
(root / "context-lock.json").write_text('{"context_id":"test-context"}\n', encoding="utf-8")
source = root / "linuxcnc/src/emc/kinematics"
source.mkdir(parents=True)
(source / "trivkins.c").write_text("int kinematicsForward(void) { return 0; }\n", encoding="utf-8")
(source / "kinematics.h").write_text("typedef int KINEMATICS_TYPE;\n", encoding="utf-8")
config = root / "linuxcnc/configs/sim/demo"
config.mkdir(parents=True)
(config / "demo.ini").write_text("[KINS]\nKINEMATICS = trivkins coordinates=XYZ\n", encoding="utf-8")
(config / "demo.hal").write_text("loadrt trivkins\n", encoding="utf-8")
(root / "linuxcnc/tests/matrixkins").mkdir(parents=True)
(root / "linuxcnc/tests/matrixkins/test.ini").write_text("[KINS]\nKINEMATICS = matrixkins\n", encoding="utf-8")
return directory, root
def test_report_is_deterministic_and_retains_missing_required_root(self):
directory, root = self.make_tree()
self.addCleanup(directory.cleanup)
first = root / "one.json"
second = root / "two.json"
report_one = generate(root, first)
report_two = generate(root, second)
self.assertEqual(report_one["status"], "blocked")
self.assertIn("linuxcnc/unit_tests/kinematics", report_one["missing_required_test_roots"])
self.assertEqual(first.read_bytes(), second.read_bytes())
self.assertEqual(report_one["source"]["file_count"], 2)
self.assertEqual(report_one["configuration"]["entry_count"], 2)
self.assertEqual(report_one["hal"]["entry_count"], 1)
self.assertTrue(report_one["configuration"]["entries"][0]["config_id"].startswith("config:"))
self.assertEqual(report_one["source"]["files"][0]["source_id"], "source:linuxcnc/src/emc/kinematics/kinematics.h")
self.assertTrue(all(row["oracle"]["status"] == "blocked" for row in report_one["families"]))
self.assertEqual(json.loads(first.read_text(encoding="utf-8")), report_two)
def test_hal_transform_and_switchkins_entries_are_registered(self):
directory, root = self.make_tree()
self.addCleanup(directory.cleanup)
hal = root / "linuxcnc/configs/sim/demo/demo.hal"
hal.write_text("loadrt corexy_by_hal names=corexy\nnet select => motion.switchkins-type\n", encoding="utf-8")
report = generate(root, root / "report.json")
kinds = {row["kind"] for row in report["hal"]["entries"]}
self.assertIn("loadrt", kinds)
self.assertIn("hal_transform", kinds)
self.assertIn("switchkins", kinds)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,80 @@
import json
import sys
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
sys.path.insert(0, str(Path(__file__).resolve().parent))
from generate_kinematics_config_graph import generate
class KinematicsConfigGraphTests(unittest.TestCase):
def make_tree(self):
directory = TemporaryDirectory()
root = Path(directory.name)
coverage = root / "coverage"
coverage.mkdir()
config = root / "linuxcnc/configs/sim/demo"
config.mkdir(parents=True)
(config / "demo.ini").write_text(
"[KINS]\nKINEMATICS = trivkins\n[HAL]\nHALFILE = demo.hal\n",
encoding="utf-8",
)
(config / "demo.hal").write_text(
"loadrt [KINS]KINEMATICS\nnet select => motion.switchkins-type\n",
encoding="utf-8",
)
matrix = root / "linuxcnc/tests/matrixkins"
matrix.mkdir(parents=True)
(matrix / "test.ini").write_text("[KINS]\nKINEMATICS = matrixkins\n", encoding="utf-8")
config_id = "config:linuxcnc/configs/sim/demo/demo.ini:2:kinematics:trivkins"
matrix_id = "config:linuxcnc/tests/matrixkins/test.ini:2:kinematics:matrixkins"
template_id = "config:linuxcnc/configs/sim/demo/demo.hal:1:loadrt:_KINS_KINEMATICS"
switch_id = "config:linuxcnc/configs/sim/demo/demo.hal:2:switchkins:motion.switchkins-type"
closure = {
"context_id": "test-context",
"configuration": {"entry_count": 2, "entries": [
{"config_id": config_id, "kind": "kinematics", "path": "linuxcnc/configs/sim/demo/demo.ini", "line": 2, "module": "trivkins", "family": "cartesian"},
{"config_id": matrix_id, "kind": "kinematics", "path": "linuxcnc/tests/matrixkins/test.ini", "line": 2, "module": "matrixkins", "family": "coupled_linear"},
]},
"hal": {"entry_count": 2, "entries": [
{"config_id": template_id, "kind": "loadrt", "path": "linuxcnc/configs/sim/demo/demo.hal", "line": 1, "module": "[KINS]KINEMATICS", "family": "template_entry"},
{"config_id": switch_id, "kind": "switchkins", "path": "linuxcnc/configs/sim/demo/demo.hal", "line": 2, "module": "motion.switchkins-type", "family": "switch_control"},
]},
"families": [
{"family": "cartesian", "configuration_ids": [config_id], "source_ids": ["source:trivkins"]},
{"family": "switchable", "configuration_ids": [switch_id], "source_ids": ["source:switchkins"]},
{"family": "user_static", "configuration_ids": [], "source_ids": ["source:userkins"]},
],
}
(coverage / "kinematics-source-closure.report.json").write_text(json.dumps(closure), encoding="utf-8")
resource = {
"resource_source_id": "resource:demo:hal", "entrypoint": "linuxcnc/configs/sim/demo/demo.ini",
"path": "linuxcnc/configs/sim/demo/demo.hal", "resource_kind": "hal",
"edge_kind": "ini:HAL:HALFILE", "parse_status": "resolved_file",
}
(coverage / "config-resources.jsonl").write_text(json.dumps(resource) + "\n", encoding="utf-8")
(coverage / "source-index.jsonl").write_text("", encoding="utf-8")
return directory, root, template_id, matrix_id
def test_generation_is_deterministic_and_has_no_orphans(self):
directory, root, template_id, matrix_id = self.make_tree()
self.addCleanup(directory.cleanup)
output, samples_output = root / "report.json", root / "samples.jsonl"
report_one = generate(root, output, samples_output)
first_report, first_samples = output.read_bytes(), samples_output.read_bytes()
report_two = generate(root, output, samples_output)
self.assertEqual(first_report, output.read_bytes())
self.assertEqual(first_samples, samples_output.read_bytes())
self.assertEqual(report_one["closure"]["orphan_entry_ids"], [])
self.assertEqual(report_one["closure"]["unowned_template_ids"], [])
self.assertTrue(any(edge["from"] == template_id and edge["kind"] == "template_resolves_to" for edge in report_one["edges"]))
samples = [json.loads(line) for line in samples_output.read_text(encoding="utf-8").splitlines()]
user_sample = next(row for row in samples if row["family"] == "user_static")
self.assertEqual(user_sample["configuration_id"], matrix_id)
self.assertEqual(report_one, report_two)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,19 @@
import json
import unittest
from pathlib import Path
class KinematicsMappingContractTests(unittest.TestCase):
def test_contract_records_duplicate_and_home_semantics(self):
root = Path(__file__).resolve().parents[1]
report = json.loads((root / "coverage/kinematics-mapping-contract.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "kinematics-mapping-contract.v1")
self.assertEqual(report["status"], "verified")
duplicate = next(row for row in report["sample_mappings"] if row["coordinates"] == "XYYZ")
self.assertEqual(duplicate["duplicate"]["owner"], 1)
self.assertIn("home", report["semantics"])
self.assertIn("enabled_limit_violation", report["error_cases"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,18 @@
import json
import unittest
from pathlib import Path
class KinematicsNumericPolicyTests(unittest.TestCase):
def test_policy_is_verified_and_exact_fields_are_declared(self):
root = Path(__file__).resolve().parents[1]
report = json.loads((root / "coverage/kinematics-numeric-policy.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "kinematics-numeric-policy.v1")
self.assertEqual(report["status"], "verified")
self.assertFalse(report["compiler"]["fast_math"])
self.assertIn("SIM_KIN_ERR_SINGULAR", {row["expected"] for row in report["boundary_matrix"]})
self.assertIn("branch_id", report["exact_fields"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,44 @@
import json
import sys
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
sys.path.insert(0, str(Path(__file__).resolve().parent))
from generate_kinematics_oracle_report import generate
class KinematicsOracleReportTests(unittest.TestCase):
def make_tree(self):
directory = TemporaryDirectory()
root = Path(directory.name)
coverage = root / "coverage"
coverage.mkdir()
closure = {
"context_id": "test-context", "missing_required_test_roots": ["linuxcnc/unit_tests/kinematics"],
}
(coverage / "kinematics-source-closure.report.json").write_text(json.dumps(closure), encoding="utf-8")
sample = {
"sample_id": "kinematics-sample:cartesian:nominal-roundtrip", "family": "cartesian",
"configuration_id": "config:demo", "operation": "fk_ik_fk_roundtrip", "input": {"joint": [1.0]},
"expected": {"invariant": "pose_roundtrip", "absolute_tolerance": 1e-9},
"execution": {"status": "blocked", "reason": "missing official oracle"},
}
(coverage / "kinematics-oracle-samples.jsonl").write_text(json.dumps(sample) + "\n", encoding="utf-8")
return directory, root
def test_missing_official_root_blocks_without_fake_golden(self):
directory, root = self.make_tree()
self.addCleanup(directory.cleanup)
report = generate(root, root / "report.json", root / "schema.json")
self.assertEqual(report["status"], "blocked")
self.assertEqual(report["sample_count"], 1)
self.assertEqual(report["ready_count"], 0)
self.assertEqual(report["records"][0]["result"]["error_code"], "MISSING_OFFICIAL_ORACLE")
self.assertEqual(report["records"][0]["execution"]["status"], "blocked")
self.assertEqual(json.loads((root / "schema.json").read_text(encoding="utf-8"))["$id"], "linuxcnc-web-wasm/kinematics-golden.v1")
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,51 @@
import json
import subprocess
import unittest
from pathlib import Path
import generate_kinematics_runtime_report
ROOT = Path(__file__).resolve().parents[1]
class KinematicsRuntimeTests(unittest.TestCase):
def test_native_kinematics_binary(self):
binary = ROOT / "build-native/sim_kinematics_test"
if not binary.is_file():
self.skipTest("native build has not been generated")
result = subprocess.run([str(binary)], cwd=ROOT, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.assertEqual(result.returncode, 0, result.stderr.decode(errors="replace"))
def test_wasm_exports_kinematics_api(self):
module = ROOT / "build-wasm/linuxcnc-sim-core.js"
if not module.is_file():
self.skipTest("WASM build has not been generated")
text = module.read_text(encoding="utf-8")
for symbol in ("sim_kinematics_init", "sim_kinematics_forward_ex", "sim_kinematics_inverse_ex", "sim_kinematics_state_hash"):
self.assertIn(symbol, text)
def test_report_contract(self):
report = json.loads((ROOT / "coverage/kinematics-runtime.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "kinematics-runtime-report.v1")
self.assertEqual(report["status"], "partial")
self.assertTrue(report["registered_build_targets"]["native_ctest"])
self.assertFalse(report["closure"]["full_linuxcnc_kinematics"])
self.assertIn("linear-delta", report["supported_types"])
self.assertIn("rotary-delta", report["supported_types"])
self.assertIn("tripod", report["supported_types"])
def test_report_is_deterministic(self):
first = ROOT / "coverage/.kinematics-runtime-one.json"
second = ROOT / "coverage/.kinematics-runtime-two.json"
try:
report_one = generate_kinematics_runtime_report.generate(ROOT, first)
report_two = generate_kinematics_runtime_report.generate(ROOT, second)
self.assertEqual(report_one, report_two)
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env python3
import json
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
from model_stream import SSEDecoder, StreamProtocolError, consume_sse
def event(kind, payload):
return f"event: {kind}\ndata: {json.dumps(payload, separators=(',', ':'))}\n\n".encode()
def completed(*, output_tokens=2, status="completed", incomplete_reason=None):
response = {
"id": "resp-1",
"status": status,
"usage": {
"input_tokens": 20,
"output_tokens": output_tokens,
"total_tokens": 20 + output_tokens,
},
}
if incomplete_reason is not None:
response["incomplete_details"] = {"reason": incomplete_reason}
kind = "response.incomplete" if status == "incomplete" else "response.completed"
return event(kind, {"type": kind, "response": response})
class ModelStreamTests(unittest.TestCase):
def test_decoder_preserves_utf8_across_chunks(self):
payload = event("response.output_text.delta", {
"type": "response.output_text.delta",
"delta": "text",
})
decoder = SSEDecoder()
split = payload.index(b"text") + 2
self.assertEqual(decoder.feed(payload[:split]), [])
parsed = decoder.feed(payload[split:])
self.assertEqual(len(parsed), 1)
self.assertIn("text", parsed[0].data)
def test_completed_stream_uses_provider_usage(self):
delta = event("response.output_text.delta", {
"type": "response.output_text.delta",
"delta": "hello",
})
result = consume_sse(
[delta[:7], delta[7:], completed(output_tokens=2)],
context_id="c1",
packet_id="p1",
max_output_tokens=8,
)
self.assertEqual(result["status"], "completed")
self.assertTrue(result["reusable"])
self.assertEqual(result["usage"]["output_tokens"], 2)
def test_disconnect_writes_metadata_only_checkpoint(self):
def disconnected():
yield event("response.output_text.delta", {
"type": "response.output_text.delta",
"delta": "secret-partial-text",
})
raise ConnectionError("body ended early")
with TemporaryDirectory() as directory:
path = Path(directory) / "checkpoint.json"
result = consume_sse(
disconnected(),
context_id="c1",
packet_id="p1",
checkpoint_path=path,
max_output_tokens=64,
)
checkpoint_text = path.read_text(encoding="utf-8")
self.assertEqual(result["status"], "interrupted")
self.assertFalse(result["reusable"])
self.assertEqual(result["reason_code"], "stream_disconnected")
self.assertNotIn("secret-partial-text", checkpoint_text)
self.assertFalse(json.loads(checkpoint_text)["reusable"])
def test_stream_ending_without_terminal_event_is_not_reusable(self):
result = consume_sse(
[event("response.created", {"type": "response.created"})],
context_id="c1",
packet_id="p1",
)
self.assertEqual(result["reason_code"], "stream_ended_without_completion")
self.assertFalse(result["reusable"])
def test_incomplete_utf8_is_a_protocol_error(self):
result = consume_sse(
[b"data: {\"delta\":\"\xe4\xb8"],
context_id="c1",
packet_id="p1",
)
self.assertEqual(result["status"], "protocol_error")
self.assertEqual(result["reason_code"], "stream_decode_error")
def test_context_failure_requires_smaller_fresh_packet(self):
failed = event("response.failed", {
"type": "response.failed",
"response": {
"id": "resp-2",
"status": "failed",
"error": {
"code": "context_length_exceeded",
"message": "context window exceeded",
},
},
})
result = consume_sse(
[failed],
context_id="c1",
packet_id="p1",
profile="feature",
)
self.assertEqual(result["status"], "context_budget_exceeded")
self.assertEqual(result["retry"]["profile"], "doc")
self.assertFalse(result["retry"]["reuse_packet"])
def test_provider_output_overrun_is_rejected(self):
result = consume_sse(
[completed(output_tokens=9)],
context_id="c1",
packet_id="p1",
max_output_tokens=8,
)
self.assertEqual(result["status"], "output_budget_exceeded")
self.assertFalse(result["reusable"])
def test_incomplete_response_is_checkpointable_not_reusable(self):
result = consume_sse(
[completed(
output_tokens=8,
status="incomplete",
incomplete_reason="max_output_tokens",
)],
context_id="c1",
packet_id="p1",
max_output_tokens=8,
)
self.assertEqual(result["status"], "truncated")
self.assertFalse(result["reusable"])
self.assertEqual(result["finish_reason"], "max_output_tokens")
def test_decoder_enforces_event_budget(self):
decoder = SSEDecoder(max_event_bytes=8, max_response_bytes=32)
with self.assertRaisesRegex(StreamProtocolError, "event exceeds"):
decoder.feed(b"data: 123456789\n\n")
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,37 @@
import json
import subprocess
import unittest
from pathlib import Path
import generate_motion_report
ROOT = Path(__file__).resolve().parents[1]
class MotionRuntimeTests(unittest.TestCase):
def test_native_motion_binary(self):
binary = ROOT / "build-native/sim_motion_queue_test"
if not binary.is_file():
self.skipTest("native build has not been generated")
result = subprocess.run([str(binary)], cwd=ROOT, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.assertEqual(result.returncode, 0, result.stderr.decode(errors="replace"))
def test_wasm_exports_motion_api(self):
module = ROOT / "build-wasm/linuxcnc-sim-core.js"
if not module.is_file():
self.skipTest("WASM build has not been generated")
text = module.read_text(encoding="utf-8")
for symbol in ("sim_motion_init", "sim_motion_enqueue", "sim_motion_step", "sim_motion_state_hash", "sim_motion_canonical"):
self.assertIn(symbol, text)
def test_report_contract(self):
report = json.loads((ROOT / "coverage/motion-runtime.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "motion-runtime-report.v1")
self.assertGreater(report["inputs"]["file_count"], 0)
self.assertEqual(report["outputs"]["file_count"], len(generate_motion_report.OUTPUTS))
self.assertTrue(report["canonical"]["byte_identical"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,27 @@
import json
import unittest
from pathlib import Path
import generate_pentapod_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class PentapodKinematicsReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/pentapod-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "pentapod-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertTrue(report["configuration_candidate"]["scan"])
self.assertEqual(report["geometry"]["struts"], 5)
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertTrue(report["samples"]["state_hash_identical"])
self.assertEqual(report["oracle"]["status"], "blocked")
def test_deterministic(self):
first, second = ROOT / "coverage/.penta-one.json", ROOT / "coverage/.penta-two.json"
try:
self.assertEqual(generate_pentapod_kinematics_report.generate(ROOT, first), generate_pentapod_kinematics_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True); second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -19,9 +19,19 @@ class PosemathRuntimeTests(unittest.TestCase):
if not module.is_file():
self.skipTest("WASM build has not been generated")
text = module.read_text(encoding="utf-8")
for symbol in ("sim_posemath_normalize", "sim_posemath_dot", "sim_posemath_line_point"):
for symbol in ("sim_posemath_normalize", "sim_posemath_dot", "sim_posemath_line_point",
"sim_posemath_segment_validate", "sim_posemath_segment_point",
"sim_tp_init", "sim_tp_step", "sim_tp_state_hash"):
self.assertIn(symbol, text)
def test_report_records_inputs_outputs_and_determinism(self):
report = __import__("json").loads((ROOT / "coverage/posemath-runtime.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "posemath-runtime-report.v2")
self.assertGreater(report["inputs"]["file_count"], 0)
self.assertGreater(report["outputs"]["file_count"], 0)
self.assertTrue(report["determinism"]["byte_identical"])
self.assertTrue(report["determinism"]["state_hash_identical"])
def test_posemath_source_is_linked(self):
cmake = (ROOT / "wasm-core/CMakeLists.txt").read_text(encoding="utf-8")
self.assertIn("posemath.cc", cmake)

View File

@@ -0,0 +1,31 @@
import json
import unittest
from pathlib import Path
import generate_robot_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class RobotKinematicsReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/robot-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "robot-kinematics-report.v1")
self.assertTrue(all(row["scan"] for row in report["configuration_candidates"].values()))
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertEqual(report["oracle"]["status"], "blocked")
self.assertIn("workspace_derived_fixture", report["configuration_candidates"]["scorbot"]["origin"])
def test_deterministic(self):
first = ROOT / "coverage/.robot-one.json"
second = ROOT / "coverage/.robot-two.json"
try:
self.assertEqual(generate_robot_kinematics_report.generate(ROOT, first), generate_robot_kinematics_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,33 @@
import json
import unittest
from pathlib import Path
import generate_tripod_kinematics_report
ROOT = Path(__file__).resolve().parents[1]
class TripodKinematicsReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/tripod-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "tripod-kinematics-report.v1")
self.assertEqual(report["implementation_status"], "verified")
self.assertTrue(report["configuration_candidate"]["scan"])
self.assertEqual(report["geometry"]["hal_parameters"], {"Bx": 10.0, "Cx": 5.0, "Cy": 7.071})
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertTrue(report["samples"]["state_hash_identical"])
self.assertEqual(report["oracle"]["status"], "blocked")
self.assertEqual(report["workspace_and_boundaries"]["base_plane_singular_error"], -704)
def test_deterministic(self):
first = ROOT / "coverage/.tripod-one.json"
second = ROOT / "coverage/.tripod-two.json"
try:
self.assertEqual(generate_tripod_kinematics_report.generate(ROOT, first), generate_tripod_kinematics_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,18 @@
import json
import unittest
from pathlib import Path
class TrivkinsIntegrationReportTests(unittest.TestCase):
def test_report_contract(self):
root = Path(__file__).resolve().parents[1]
report = json.loads((root / "coverage/trivkins-integration.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "trivkins-integration-report.v1")
self.assertEqual(report["status"], "partial")
self.assertEqual(set(report["modes"]), {"identity", "forward_only", "inverse_only", "both"})
self.assertEqual(report["tests"]["status"], "passed")
self.assertEqual(report["oracle"]["status"], "blocked")
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,31 @@
import json
import unittest
from pathlib import Path
import generate_trt_report
ROOT = Path(__file__).resolve().parents[1]
class TrtReportTests(unittest.TestCase):
def test_contract(self):
report = json.loads((ROOT / "coverage/trt-kinematics.report.json").read_text(encoding="utf-8"))
self.assertEqual(report["schema"], "trt-kinematics-report.v1")
self.assertTrue(report["configuration_candidates"]["xyzac"]["scan"])
self.assertTrue(report["configuration_candidates"]["xyzbc"]["scan"])
self.assertTrue(report["samples"]["byte_identical_json"])
self.assertEqual(report["oracle"]["status"], "blocked")
def test_deterministic(self):
first = ROOT / "coverage/.trt-one.json"
second = ROOT / "coverage/.trt-two.json"
try:
self.assertEqual(generate_trt_report.generate(ROOT, first), generate_trt_report.generate(ROOT, second))
finally:
first.unlink(missing_ok=True)
second.unlink(missing_ok=True)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,207 @@
#!/usr/bin/env python3
import json
import unittest
from validate_model_budget import (
ModelBudgetError,
extract_response_usage,
validate_provider_response,
validate_request,
validate_response,
)
def packet(
profile="feature",
token_limit=4096,
packet_bytes=1200,
evidence_bytes=300,
byte_limit=16384,
evidence_byte_limit=None,
):
budget = {
"profile": profile,
"token_limit": token_limit,
"byte_limit": byte_limit,
"packet_bytes": packet_bytes,
"selected_evidence_bytes": evidence_bytes,
}
if evidence_byte_limit is not None:
budget["evidence_byte_limit"] = evidence_byte_limit
return {
"budget": budget
}
class ModelBudgetTests(unittest.TestCase):
def test_profile_reserves_output_and_safety_margin(self):
result = validate_request(packet())
self.assertEqual(result["max_output_tokens"], 1024)
self.assertEqual(result["reserved_total_tokens"], 2780)
def test_non_packet_request_overhead_is_reserved(self):
result = validate_request(packet(), overhead_tokens=700)
self.assertEqual(result["overhead_tokens"], 700)
self.assertEqual(result["reserved_total_tokens"], 3480)
def test_request_overhead_can_exhaust_context_window(self):
result = validate_request(packet(), context_window=4000, overhead_tokens=700)
self.assertEqual(result["estimated_request_input_tokens"], 2200)
def test_context_window_overflow_fails_before_request(self):
with self.assertRaisesRegex(ModelBudgetError, "context budget exceeded"):
validate_request(packet(), context_window=1700)
def test_estimated_input_must_fit_declared_packet_budget(self):
with self.assertRaisesRegex(ModelBudgetError, "input token budget exceeded"):
validate_request(packet(token_limit=400), input_tokens=401)
def test_byte_upper_bound_prevents_unsafe_heuristic_underestimate(self):
with self.assertRaisesRegex(ModelBudgetError, "input token budget exceeded"):
validate_request(packet(token_limit=1499))
def test_exact_tokenizer_count_can_be_supplied(self):
result = validate_request(packet(token_limit=600), input_tokens=500)
self.assertEqual(result["estimated_input_tokens"], 500)
def test_explicit_output_budget_can_be_smaller(self):
result = validate_request(packet(), context_window=5000, max_output_tokens=256)
self.assertEqual(result["max_output_tokens"], 256)
def test_output_budget_cannot_expand_profile(self):
with self.assertRaisesRegex(ModelBudgetError, "output token budget exceeds profile"):
validate_request(packet(), context_window=20000, max_output_tokens=2048)
def test_input_budget_cannot_expand_profile(self):
with self.assertRaisesRegex(ModelBudgetError, "packet token limit exceeds profile"):
validate_request(packet(token_limit=5000), context_window=20000)
def test_byte_budget_cannot_expand_profile(self):
with self.assertRaisesRegex(ModelBudgetError, "byte limit exceeds profile"):
validate_request(packet(byte_limit=16385), context_window=20000)
def test_evidence_budget_is_checked_even_without_envelope(self):
with self.assertRaisesRegex(ModelBudgetError, "selected evidence exceeds its evidence byte budget"):
validate_request(packet(evidence_bytes=301, evidence_byte_limit=300))
def test_nested_accumulated_history_is_rejected(self):
value = packet()
value["scope"] = {"state": {"messages": ["old"]}}
with self.assertRaisesRegex(ModelBudgetError, "forbidden accumulated context"):
validate_request(value)
def test_full_packet_rejects_unknown_top_level_context(self):
value = packet()
value.update({"context_id": "c1", "packet_id": "p1", "tool_output": "log"})
with self.assertRaisesRegex(ModelBudgetError, "unknown top-level context field"):
validate_request(value)
def test_packet_byte_overflow_is_rejected(self):
with self.assertRaisesRegex(ModelBudgetError, "byte budget"):
validate_request(packet(packet_bytes=16000, evidence_bytes=1000))
def test_full_packet_rejects_stale_serialized_size(self):
value = packet()
value.update({
"context_id": "c1",
"packet_id": "p1",
"evidence": {"selected": [], "excluded": []},
})
with self.assertRaisesRegex(ModelBudgetError, "does not match serialized packet"):
validate_request(value)
def test_packet_without_ids_cannot_lie_about_serialized_size(self):
value = packet()
value["task"] = "x" * 20000
with self.assertRaisesRegex(ModelBudgetError, "does not match serialized packet"):
validate_request(value)
def test_full_packet_rejects_inconsistent_evidence_totals(self):
value = packet(packet_bytes=215, evidence_bytes=5)
value.update({
"context_id": "c1",
"packet_id": "p1",
"evidence": {"selected": [{"path": "x", "bytes": 4}], "excluded": []},
})
with self.assertRaisesRegex(ModelBudgetError, "selected evidence byte metadata"):
validate_request(value)
def test_full_packet_rejects_accumulated_history(self):
value = packet()
value["history"] = ["old response"]
with self.assertRaisesRegex(ModelBudgetError, "forbidden accumulated context"):
validate_request(value)
def test_full_packet_rejects_unknown_exclusion_reason(self):
value = packet(packet_bytes=203, evidence_bytes=0)
value.update({
"context_id": "c1",
"packet_id": "p1",
"evidence": {"selected": [], "excluded": [{"path": "x", "reason_code": "old"}]},
})
# Stabilize the serialized size after adding the evidence envelope.
for _ in range(4):
value["budget"]["packet_bytes"] = len(
json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
)
with self.assertRaisesRegex(ModelBudgetError, "unknown reason code"):
validate_request(value)
def test_response_overrun_is_rejected(self):
with self.assertRaisesRegex(ModelBudgetError, "response exceeded output token budget"):
validate_response(1025, max_output_tokens=1024)
def test_response_length_finish_is_checkpointable(self):
result = validate_response(1024, max_output_tokens=1024, finish_reason="length")
self.assertTrue(result["truncated"])
def test_responses_api_incomplete_reason_is_checkpointable(self):
result = validate_response(
1024,
max_output_tokens=1024,
finish_reason="max_output_tokens",
)
self.assertTrue(result["truncated"])
def test_stream_envelope_usage_is_validated(self):
event = {
"type": "response.completed",
"response": {
"status": "completed",
"usage": {
"input_tokens": 300,
"output_tokens": 40,
"total_tokens": 340,
},
},
}
self.assertEqual(extract_response_usage(event)["output_tokens"], 40)
result = validate_provider_response(event, max_output_tokens=64)
self.assertEqual(result["status"], "completed")
def test_provider_usage_total_cannot_be_inconsistent(self):
response = {
"status": "completed",
"usage": {"input_tokens": 10, "output_tokens": 5, "total_tokens": 14},
}
with self.assertRaisesRegex(ModelBudgetError, "total is smaller"):
validate_provider_response(response, max_output_tokens=16)
def test_failed_provider_response_is_not_reusable(self):
response = {
"status": "failed",
"usage": {"input_tokens": 10, "output_tokens": 0, "total_tokens": 10},
}
with self.assertRaisesRegex(ModelBudgetError, "not reusable"):
validate_provider_response(response, max_output_tokens=16)
def test_provider_response_without_completion_status_is_rejected(self):
response = {
"usage": {"input_tokens": 10, "output_tokens": 1, "total_tokens": 11},
}
with self.assertRaisesRegex(ModelBudgetError, "missing completion status"):
validate_provider_response(response, max_output_tokens=16)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python3
import tempfile
import unittest
import json
from pathlib import Path
from validate_task_pointer import PointerError, validate_pointer
VALID = """# pointer
## Current
context_id: c1
## Read
read paths only; coverage/report.json
## Task
one result
## Next
one next
## Verify
one test
## Stop
stop on drift
"""
class TaskPointerTests(unittest.TestCase):
def write(self, text: str) -> Path:
directory = tempfile.TemporaryDirectory()
self.addCleanup(directory.cleanup)
path = Path(directory.name) / "nextTask.md"
path.write_text(text, encoding="utf-8")
return path
def test_valid_pointer_reports_bounded_size(self):
result = validate_pointer(self.write(VALID))
self.assertLessEqual(result["bytes"], 8192)
def test_context_id_must_match_lock_when_supplied(self):
directory = tempfile.TemporaryDirectory()
self.addCleanup(directory.cleanup)
root = Path(directory.name)
pointer = root / "nextTask.md"
pointer.write_text(VALID, encoding="utf-8")
lock = root / "context-lock.json"
lock.write_text(json.dumps({"context_id": "other"}), encoding="utf-8")
with self.assertRaisesRegex(PointerError, "does not match lock"):
validate_pointer(pointer, lock_path=lock)
def test_history_heading_is_rejected(self):
with self.assertRaisesRegex(PointerError, "history section"):
validate_pointer(self.write(VALID + "\n## History\nold log\n"))
def test_evidence_heading_is_rejected(self):
with self.assertRaisesRegex(PointerError, "history section"):
validate_pointer(self.write(VALID + "\n## Evidence\nold evidence\n"))
def test_large_pointer_is_rejected(self):
with self.assertRaisesRegex(PointerError, "byte budget"):
validate_pointer(self.write(VALID + "x" * 9000))
if __name__ == "__main__":
unittest.main()

View File

@@ -189,6 +189,301 @@ def validate(repo_root: Path, lock_path: Path, source_path: Path, manifest_path:
errors.append("posemath runtime report is missing")
elif posemath_report_hash != sha256(posemath_report_path):
errors.append("posemath runtime report hash does not match context lock")
interpreter_report_path = repo_root / "coverage/interpreter-runtime.report.json"
interpreter_report_hash = lock.get("interpreter_runtime_report_sha256")
if interpreter_report_hash:
if not interpreter_report_path.is_file():
errors.append("interpreter runtime report is missing")
elif interpreter_report_hash != sha256(interpreter_report_path):
errors.append("interpreter runtime report hash does not match context lock")
motion_report_path = repo_root / "coverage/motion-runtime.report.json"
motion_report_hash = lock.get("motion_runtime_report_sha256")
if motion_report_hash:
if not motion_report_path.is_file():
errors.append("motion runtime report is missing")
elif motion_report_hash != sha256(motion_report_path):
errors.append("motion runtime report hash does not match context lock")
kinematics_runtime_report_path = repo_root / "coverage/kinematics-runtime.report.json"
kinematics_runtime_report_hash = lock.get("kinematics_runtime_report_sha256")
if kinematics_runtime_report_hash:
if not kinematics_runtime_report_path.is_file():
errors.append("kinematics runtime report is missing")
elif kinematics_runtime_report_hash != sha256(kinematics_runtime_report_path):
errors.append("kinematics runtime report hash does not match context lock")
kinematics_closure_path = repo_root / "coverage/kinematics-source-closure.report.json"
kinematics_closure_hash = lock.get("kinematics_source_closure_report_sha256")
if kinematics_closure_hash:
if not kinematics_closure_path.is_file():
errors.append("kinematics source closure report is missing")
elif kinematics_closure_hash != sha256(kinematics_closure_path):
errors.append("kinematics source closure report hash does not match context lock")
if kinematics_closure_path.is_file() and isinstance(inventory_count, dict):
try:
kinematics_report = json.loads(kinematics_closure_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics source closure report is not valid JSON")
else:
expected_counts = {
"kinematics_source_files": kinematics_report.get("source", {}).get("implementation_file_count"),
"kinematics_config_entries": kinematics_report.get("configuration", {}).get("entry_count"),
"kinematics_hal_entries": kinematics_report.get("hal", {}).get("entry_count"),
"kinematics_test_files": kinematics_report.get("tests", {}).get("file_count"),
}
for field, value in expected_counts.items():
if value is not None and inventory_count.get(field) != value:
errors.append(f"{field} count does not match kinematics closure report")
kinematics_graph_path = repo_root / "coverage/kinematics-config-graph.report.json"
kinematics_graph_hash = lock.get("kinematics_config_graph_report_sha256")
if kinematics_graph_hash:
if not kinematics_graph_path.is_file():
errors.append("kinematics config graph report is missing")
elif kinematics_graph_hash != sha256(kinematics_graph_path):
errors.append("kinematics config graph report hash does not match context lock")
samples_path = repo_root / "coverage/kinematics-oracle-samples.jsonl"
samples_hash = lock.get("kinematics_oracle_samples_sha256")
if samples_hash:
if not samples_path.is_file():
errors.append("kinematics oracle samples are missing")
elif samples_hash != sha256(samples_path):
errors.append("kinematics oracle samples hash does not match context lock")
oracle_report_path = repo_root / "coverage/kinematics-oracle.report.json"
oracle_report_hash = lock.get("kinematics_oracle_report_sha256")
if oracle_report_hash:
if not oracle_report_path.is_file():
errors.append("kinematics oracle report is missing")
elif oracle_report_hash != sha256(oracle_report_path):
errors.append("kinematics oracle report hash does not match context lock")
oracle_schema_path = repo_root / "coverage/kinematics-golden-schema.json"
oracle_schema_hash = lock.get("kinematics_golden_schema_sha256")
if oracle_schema_hash:
if not oracle_schema_path.is_file():
errors.append("kinematics golden schema is missing")
elif oracle_schema_hash != sha256(oracle_schema_path):
errors.append("kinematics golden schema hash does not match context lock")
if kinematics_graph_path.is_file() and isinstance(inventory_count, dict):
try:
graph = json.loads(kinematics_graph_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics config graph report is not valid JSON")
else:
graph_counts = graph.get("counts", {})
for field, report_field in (("kinematics_graph_entries", "entry_nodes"), ("kinematics_graph_edges", "edges"), ("kinematics_oracle_samples", "samples")):
value = graph_counts.get(report_field)
if value is not None and inventory_count.get(field) != value:
errors.append(f"{field} count does not match kinematics config graph report")
if oracle_report_path.is_file() and isinstance(inventory_count, dict):
try:
oracle_report = json.loads(oracle_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics oracle report is not valid JSON")
else:
if oracle_report.get("sample_count") is not None and inventory_count.get("kinematics_oracle_samples") != oracle_report.get("sample_count"):
errors.append("kinematics oracle sample count does not match oracle report")
abi_contract_path = repo_root / "coverage/kinematics-abi-contract.json"
abi_contract_hash = lock.get("kinematics_abi_contract_sha256")
if abi_contract_hash:
if not abi_contract_path.is_file():
errors.append("kinematics ABI contract is missing")
elif abi_contract_hash != sha256(abi_contract_path):
errors.append("kinematics ABI contract hash does not match context lock")
if abi_contract_path.is_file():
try:
abi_contract = json.loads(abi_contract_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics ABI contract is not valid JSON")
else:
if abi_contract.get("status") != "verified":
errors.append("kinematics ABI contract is not verified")
mapping_contract_path = repo_root / "coverage/kinematics-mapping-contract.json"
mapping_contract_hash = lock.get("kinematics_mapping_contract_sha256")
if mapping_contract_hash:
if not mapping_contract_path.is_file():
errors.append("kinematics mapping contract is missing")
elif mapping_contract_hash != sha256(mapping_contract_path):
errors.append("kinematics mapping contract hash does not match context lock")
if mapping_contract_path.is_file():
try:
mapping_contract = json.loads(mapping_contract_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics mapping contract is not valid JSON")
else:
if mapping_contract.get("status") != "verified":
errors.append("kinematics mapping contract is not verified")
numeric_policy_path = repo_root / "coverage/kinematics-numeric-policy.json"
numeric_policy_hash = lock.get("kinematics_numeric_policy_sha256")
if numeric_policy_hash:
if not numeric_policy_path.is_file():
errors.append("kinematics numeric policy is missing")
elif numeric_policy_hash != sha256(numeric_policy_path):
errors.append("kinematics numeric policy hash does not match context lock")
if numeric_policy_path.is_file():
try:
numeric_policy = json.loads(numeric_policy_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("kinematics numeric policy is not valid JSON")
else:
if numeric_policy.get("status") != "verified":
errors.append("kinematics numeric policy is not verified")
trivkins_report_path = repo_root / "coverage/trivkins-integration.report.json"
trivkins_report_hash = lock.get("trivkins_integration_report_sha256")
if trivkins_report_hash:
if not trivkins_report_path.is_file():
errors.append("trivkins integration report is missing")
elif trivkins_report_hash != sha256(trivkins_report_path):
errors.append("trivkins integration report hash does not match context lock")
coupled_report_path = repo_root / "coverage/coupled-kinematics.report.json"
coupled_report_hash = lock.get("coupled_kinematics_report_sha256")
if coupled_report_hash:
if not coupled_report_path.is_file():
errors.append("coupled kinematics report is missing")
elif coupled_report_hash != sha256(coupled_report_path):
errors.append("coupled kinematics report hash does not match context lock")
if coupled_report_path.is_file():
try:
coupled_report = json.loads(coupled_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("coupled kinematics report is not valid JSON")
else:
if coupled_report.get("implementation_status") != "verified":
errors.append("coupled kinematics implementation is not verified")
if coupled_report.get("closure", {}).get("official_oracle") != "blocked":
errors.append("coupled kinematics oracle status must remain explicitly blocked")
corexy_hal_report_path = repo_root / "coverage/corexy-hal.report.json"
corexy_hal_report_hash = lock.get("corexy_hal_report_sha256")
if corexy_hal_report_hash:
if not corexy_hal_report_path.is_file():
errors.append("corexy HAL report is missing")
elif corexy_hal_report_hash != sha256(corexy_hal_report_path):
errors.append("corexy HAL report hash does not match context lock")
if corexy_hal_report_path.is_file():
try:
corexy_hal_report = json.loads(corexy_hal_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("corexy HAL report is not valid JSON")
else:
if corexy_hal_report.get("implementation_status") != "verified":
errors.append("corexy HAL implementation is not verified")
if corexy_hal_report.get("oracle", {}).get("status") != "blocked":
errors.append("corexy HAL oracle status must remain explicitly blocked")
fiveaxis_report_path = repo_root / "coverage/fiveaxis-kinematics.report.json"
fiveaxis_report_hash = lock.get("fiveaxis_kinematics_report_sha256")
if fiveaxis_report_hash:
if not fiveaxis_report_path.is_file():
errors.append("fiveaxis kinematics report is missing")
elif fiveaxis_report_hash != sha256(fiveaxis_report_path):
errors.append("fiveaxis kinematics report hash does not match context lock")
if fiveaxis_report_path.is_file():
try:
fiveaxis_report = json.loads(fiveaxis_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("fiveaxis kinematics report is not valid JSON")
else:
if fiveaxis_report.get("implementation_status") != "verified":
errors.append("fiveaxis kinematics implementation is not verified")
if fiveaxis_report.get("oracle", {}).get("status") != "blocked":
errors.append("fiveaxis kinematics oracle status must remain explicitly blocked")
trt_report_path = repo_root / "coverage/trt-kinematics.report.json"
trt_report_hash = lock.get("trt_kinematics_report_sha256")
if trt_report_hash:
if not trt_report_path.is_file():
errors.append("TRT kinematics report is missing")
elif trt_report_hash != sha256(trt_report_path):
errors.append("TRT kinematics report hash does not match context lock")
if trt_report_path.is_file():
try:
trt_report = json.loads(trt_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("TRT kinematics report is not valid JSON")
else:
if trt_report.get("implementation_status") != "verified":
errors.append("TRT kinematics implementation is not verified")
if trt_report.get("oracle", {}).get("status") != "blocked":
errors.append("TRT kinematics oracle status must remain explicitly blocked")
robot_report_path = repo_root / "coverage/robot-kinematics.report.json"
robot_report_hash = lock.get("robot_kinematics_report_sha256")
if robot_report_hash:
if not robot_report_path.is_file():
errors.append("robot kinematics report is missing")
elif robot_report_hash != sha256(robot_report_path):
errors.append("robot kinematics report hash does not match context lock")
if robot_report_path.is_file():
try:
robot_report = json.loads(robot_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("robot kinematics report is not valid JSON")
else:
if robot_report.get("implementation_status") != "verified":
errors.append("robot kinematics implementation is not verified")
if robot_report.get("oracle", {}).get("status") != "blocked":
errors.append("robot kinematics oracle status must remain explicitly blocked")
genser_report_path = repo_root / "coverage/genser-kinematics.report.json"
genser_report_hash = lock.get("genser_kinematics_report_sha256")
if genser_report_hash:
if not genser_report_path.is_file():
errors.append("genser kinematics report is missing")
elif genser_report_hash != sha256(genser_report_path):
errors.append("genser kinematics report hash does not match context lock")
if genser_report_path.is_file():
try:
genser_report = json.loads(genser_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("genser kinematics report is not valid JSON")
else:
if genser_report.get("implementation_status") != "verified":
errors.append("genser kinematics implementation is not verified")
if genser_report.get("oracle", {}).get("status") != "blocked":
errors.append("genser kinematics oracle status must remain explicitly blocked")
delta_report_path = repo_root / "coverage/delta-kinematics.report.json"
delta_report_hash = lock.get("delta_kinematics_report_sha256")
if delta_report_hash:
if not delta_report_path.is_file():
errors.append("delta kinematics report is missing")
elif delta_report_hash != sha256(delta_report_path):
errors.append("delta kinematics report hash does not match context lock")
if delta_report_path.is_file():
try:
delta_report = json.loads(delta_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("delta kinematics report is not valid JSON")
else:
if delta_report.get("implementation_status") != "verified":
errors.append("delta kinematics implementation is not verified")
if delta_report.get("oracle", {}).get("status") != "blocked":
errors.append("delta kinematics oracle status must remain explicitly blocked")
tripod_report_path = repo_root / "coverage/tripod-kinematics.report.json"
tripod_report_hash = lock.get("tripod_kinematics_report_sha256")
if tripod_report_hash:
if not tripod_report_path.is_file():
errors.append("tripod kinematics report is missing")
elif tripod_report_hash != sha256(tripod_report_path):
errors.append("tripod kinematics report hash does not match context lock")
if tripod_report_path.is_file():
try:
tripod_report = json.loads(tripod_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("tripod kinematics report is not valid JSON")
else:
if tripod_report.get("implementation_status") != "verified":
errors.append("tripod kinematics implementation is not verified")
if tripod_report.get("oracle", {}).get("status") != "blocked":
errors.append("tripod kinematics oracle status must remain explicitly blocked")
genhex_report_path = repo_root / "coverage/genhex-kinematics.report.json"
genhex_report_hash = lock.get("genhex_kinematics_report_sha256")
if genhex_report_hash:
if not genhex_report_path.is_file():
errors.append("genhex kinematics report is missing")
elif genhex_report_hash != sha256(genhex_report_path):
errors.append("genhex kinematics report hash does not match context lock")
if genhex_report_path.is_file():
try:
genhex_report = json.loads(genhex_report_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError):
errors.append("genhex kinematics report is not valid JSON")
else:
if genhex_report.get("implementation_status") != "verified":
errors.append("genhex kinematics implementation is not verified")
if genhex_report.get("oracle", {}).get("status") != "blocked":
errors.append("genhex kinematics oracle status must remain explicitly blocked")
source_ids = [str(row.get("source_id", "")) for row in sources]
if len(source_ids) != len(set(source_ids)):
errors.append("source index contains duplicate source_id")

View File

@@ -0,0 +1,501 @@
#!/usr/bin/env python3
"""Check that a bounded context packet leaves room for model output.
This is deliberately provider-neutral. The caller supplies the model's
context window; the packet's input budget and a fixed output reserve are
checked before an API request is made.
"""
from __future__ import annotations
import argparse
import json
import math
from pathlib import Path
from typing import Any
OUTPUT_TOKEN_RESERVE = {
"doc": 768,
"feature": 1024,
"contract": 1536,
"milestone": 2048,
}
PROFILE_INPUT_TOKEN_LIMIT = {
"doc": 2048,
"feature": 4096,
"contract": 6144,
"milestone": 8192,
}
PROFILE_BYTE_LIMIT = {
"doc": 8192,
"feature": 16384,
"contract": 24576,
"milestone": 32768,
}
PROFILE_EVIDENCE_BYTE_LIMIT = {
"doc": 2048,
"feature": 6144,
"contract": 10240,
"milestone": 14336,
}
PROFILE_EVIDENCE_FILE_LIMIT = {
"doc": 8,
"feature": 16,
"contract": 24,
"milestone": 32,
}
DEFAULT_CONTEXT_WINDOW = 16384
DEFAULT_SAFETY_MARGIN = 256
# Without a provider tokenizer, UTF-8 bytes are the only portable hard upper
# bound. Any multi-byte-per-token heuristic can under-count provider tokens
# for punctuation-heavy or non-ASCII input and is unsafe for a request gate.
TOKEN_BYTES_PER_TOKEN = 1
PACKET_FIELDS = {
"context_id",
"packet_id",
"task",
"header",
"baseline",
"scope",
"contracts",
"evidence",
"risks",
"next",
"forbidden",
"budget",
}
FORBIDDEN_CONTEXT_FIELDS = {
"history",
"chat",
"conversation",
"messages",
"transcript",
"previous_packet",
"prior_context",
}
EXCLUSION_REASON_CODES = {"E1", "E2", "E3", "E4", "E5", "E6"}
class ModelBudgetError(ValueError):
"""Raised when a request cannot fit input, output, and safety budgets."""
def _response_object(value: Any) -> dict[str, Any]:
"""Return a provider response object from a response or SSE envelope.
The Responses API exposes usage on the response itself, while streaming
``response.completed`` events wrap it below a ``response`` key. Keeping
this normalization here prevents callers from accidentally validating the
event envelope instead of the actual provider usage.
"""
if not isinstance(value, dict):
raise ModelBudgetError("provider response must be an object")
response = value.get("response")
if isinstance(response, dict):
return response
return value
def extract_response_usage(value: Any) -> dict[str, int]:
"""Extract authoritative token usage from a provider response envelope."""
response = _response_object(value)
usage = response.get("usage")
if not isinstance(usage, dict):
raise ModelBudgetError("provider response is missing usage")
try:
input_tokens = int(usage["input_tokens"])
output_tokens = int(usage["output_tokens"])
total_tokens = int(usage.get("total_tokens", input_tokens + output_tokens))
except (KeyError, TypeError, ValueError) as exc:
raise ModelBudgetError("provider usage has invalid token counts") from exc
if min(input_tokens, output_tokens, total_tokens) < 0:
raise ModelBudgetError("provider usage token counts must be non-negative")
if total_tokens < input_tokens + output_tokens:
raise ModelBudgetError("provider usage total is smaller than input plus output")
return {
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": total_tokens,
}
def _find_forbidden(value: Any, path: str = "") -> str | None:
"""Find accumulated conversation state, including when nested."""
if isinstance(value, dict):
for key, child in value.items():
key_text = str(key)
child_path = f"{path}.{key_text}" if path else key_text
if key_text in FORBIDDEN_CONTEXT_FIELDS:
return child_path
found = _find_forbidden(child, child_path)
if found:
return found
elif isinstance(value, list):
for index, child in enumerate(value):
found = _find_forbidden(child, f"{path}[{index}]")
if found:
return found
return None
def _packet_input_bytes(packet: dict[str, Any]) -> int:
budget = packet.get("budget")
if not isinstance(budget, dict):
raise ModelBudgetError("packet budget is missing")
try:
packet_bytes = int(budget["packet_bytes"])
evidence_bytes = int(budget["selected_evidence_bytes"])
except (KeyError, TypeError, ValueError) as exc:
raise ModelBudgetError("packet budget lacks input byte totals") from exc
if packet_bytes < 0 or evidence_bytes < 0:
raise ModelBudgetError("packet input byte totals must be non-negative")
return packet_bytes + evidence_bytes
def _canonical_packet_bytes(packet: dict[str, Any]) -> int:
"""Return the exact UTF-8 size used by the packet generator."""
return len(
json.dumps(packet, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
)
def _validate_packet_metadata(packet: dict[str, Any], budget: dict[str, Any]) -> None:
"""Reject stale or inconsistent budget metadata before a model request.
Small unit-test fixtures may contain only a budget object. Full packets
generated for a request always carry ``context_id``/``packet_id`` and an
evidence envelope, so strict consistency checks are applied to those.
"""
forbidden = _find_forbidden(packet)
if forbidden:
raise ModelBudgetError(
"packet contains forbidden accumulated context "
f"field: {forbidden}"
)
# Generated packets have a closed envelope. Reject ad-hoc top-level
# fields that could smuggle full logs, tool output or a previous request
# beside the audited eight segments.
if "budget" in packet:
unknown = sorted(set(packet) - PACKET_FIELDS)
if unknown:
raise ModelBudgetError(
"packet contains unknown top-level context field: "
f"{unknown[0]}"
)
if set(packet) != {"budget"}:
actual = _canonical_packet_bytes(packet)
declared = int(budget["packet_bytes"])
if declared != actual:
raise ModelBudgetError(
"packet byte metadata does not match serialized packet "
f"(declared={declared}, actual={actual})"
)
evidence = packet.get("evidence")
if not isinstance(evidence, dict):
return
selected = evidence.get("selected", [])
excluded = evidence.get("excluded", [])
if not isinstance(selected, list) or not isinstance(excluded, list):
raise ModelBudgetError("packet evidence must contain selected/excluded lists")
for item in excluded:
if not isinstance(item, dict) or item.get("reason_code") not in EXCLUSION_REASON_CODES:
raise ModelBudgetError("excluded evidence has an unknown reason code")
selected_bytes = 0
for item in selected:
if not isinstance(item, dict):
raise ModelBudgetError("selected evidence entries must be objects")
try:
item_bytes = int(item["bytes"])
except (KeyError, TypeError, ValueError) as exc:
raise ModelBudgetError("selected evidence entry lacks bytes") from exc
if item_bytes < 0:
raise ModelBudgetError("selected evidence bytes must be non-negative")
selected_bytes += item_bytes
if selected_bytes != int(budget["selected_evidence_bytes"]):
raise ModelBudgetError(
"selected evidence byte metadata does not match entries "
f"(declared={budget['selected_evidence_bytes']}, actual={selected_bytes})"
)
if "selected_evidence_files" in budget and int(budget["selected_evidence_files"]) != len(selected):
raise ModelBudgetError("selected evidence file count does not match entries")
if "excluded_evidence_files" in budget and int(budget["excluded_evidence_files"]) != len(excluded):
raise ModelBudgetError("excluded evidence file count does not match entries")
if "evidence_byte_limit" in budget and selected_bytes > int(budget["evidence_byte_limit"]):
raise ModelBudgetError("selected evidence exceeds its evidence byte budget")
if "evidence_file_limit" in budget and len(selected) > int(budget["evidence_file_limit"]):
raise ModelBudgetError("selected evidence exceeds its file budget")
def estimate_input_tokens(packet: dict[str, Any]) -> int:
"""Return a safe provider-neutral upper bound from UTF-8 bytes.
A tokenizer supplied by the API client may replace this estimate. The
byte-for-token bound is deliberately strict: it may reject a packet that
would fit a particular provider, but it cannot silently approve a packet
that that provider tokenizes larger than expected.
"""
return math.ceil(_packet_input_bytes(packet) / TOKEN_BYTES_PER_TOKEN)
def validate_request(
packet: dict[str, Any],
*,
context_window: int = DEFAULT_CONTEXT_WINDOW,
max_output_tokens: int | None = None,
safety_margin: int = DEFAULT_SAFETY_MARGIN,
input_tokens: int | None = None,
overhead_tokens: int = 0,
) -> dict[str, int]:
budget = packet.get("budget")
if not isinstance(budget, dict):
raise ModelBudgetError("packet budget is missing")
try:
input_limit = int(budget["token_limit"])
byte_limit = int(budget["byte_limit"])
packet_bytes = int(budget["packet_bytes"])
selected_evidence_bytes = int(budget["selected_evidence_bytes"])
except (KeyError, TypeError, ValueError) as exc:
raise ModelBudgetError("packet budget lacks required fields") from exc
profile = str(budget.get("profile", ""))
try:
profile_output_limit = OUTPUT_TOKEN_RESERVE[profile]
profile_input_limit = PROFILE_INPUT_TOKEN_LIMIT[profile]
profile_byte_limit = PROFILE_BYTE_LIMIT[profile]
profile_evidence_limit = PROFILE_EVIDENCE_BYTE_LIMIT[profile]
profile_file_limit = PROFILE_EVIDENCE_FILE_LIMIT[profile]
except KeyError as exc:
raise ModelBudgetError(f"unknown packet profile: {profile}") from exc
if max_output_tokens is None:
max_output_tokens = profile_output_limit
try:
context_window = int(context_window)
max_output_tokens = int(max_output_tokens)
safety_margin = int(safety_margin)
overhead_tokens = int(overhead_tokens)
declared_evidence_limit = int(budget.get("evidence_byte_limit", profile_evidence_limit))
declared_file_limit = int(budget.get("evidence_file_limit", profile_file_limit))
except (TypeError, ValueError) as exc:
raise ModelBudgetError("model budget fields must be integers") from exc
values = (
context_window,
max_output_tokens,
safety_margin,
input_limit,
byte_limit,
packet_bytes,
selected_evidence_bytes,
overhead_tokens,
declared_evidence_limit,
declared_file_limit,
)
if any(value < 0 for value in values):
raise ModelBudgetError("model budgets must be non-negative")
if input_limit > profile_input_limit:
raise ModelBudgetError(
"packet token limit exceeds profile "
f"(declared={input_limit}, profile={profile_input_limit})"
)
if byte_limit > profile_byte_limit:
raise ModelBudgetError(
"packet byte limit exceeds profile "
f"(declared={byte_limit}, profile={profile_byte_limit})"
)
if declared_evidence_limit > profile_evidence_limit:
raise ModelBudgetError(
"evidence byte limit exceeds profile "
f"(declared={declared_evidence_limit}, profile={profile_evidence_limit})"
)
if declared_file_limit > profile_file_limit:
raise ModelBudgetError(
"evidence file limit exceeds profile "
f"(declared={declared_file_limit}, profile={profile_file_limit})"
)
if max_output_tokens > profile_output_limit:
raise ModelBudgetError(
"output token budget exceeds profile "
f"(requested={max_output_tokens}, profile={profile_output_limit})"
)
try:
_validate_packet_metadata(packet, budget)
except ModelBudgetError:
raise
except (KeyError, TypeError, ValueError) as exc:
raise ModelBudgetError("packet budget metadata is malformed") from exc
context_bytes = packet_bytes + selected_evidence_bytes
if packet_bytes > byte_limit or context_bytes > byte_limit:
raise ModelBudgetError("packet exceeds its declared byte budget")
if selected_evidence_bytes > declared_evidence_limit:
raise ModelBudgetError("selected evidence exceeds its evidence byte budget")
if selected_evidence_bytes > profile_evidence_limit:
raise ModelBudgetError("selected evidence exceeds profile byte budget")
try:
estimated_input = estimate_input_tokens(packet) if input_tokens is None else int(input_tokens)
except (TypeError, ValueError) as exc:
raise ModelBudgetError("input token count must be an integer") from exc
if estimated_input < 0:
raise ModelBudgetError("input token count must be non-negative")
if estimated_input > input_limit:
raise ModelBudgetError(
"packet input token budget exceeded "
f"(estimated={estimated_input}, limit={input_limit})"
)
# Reserve the input actually sent for this request plus fixed provider
# overhead. Reserving the full profile here rejects small packets for no
# reason, while the packet's own input_limit still prevents later growth.
effective_input = estimated_input + overhead_tokens
available = context_window - safety_margin - max_output_tokens
if effective_input > available:
raise ModelBudgetError(
"model context budget exceeded "
f"(input={effective_input}, output={max_output_tokens}, "
f"safety={safety_margin}, window={context_window})"
)
return {
"context_window": context_window,
"input_token_limit": input_limit,
"estimated_input_tokens": estimated_input,
"overhead_tokens": overhead_tokens,
"estimated_request_input_tokens": estimated_input + overhead_tokens,
"max_output_tokens": max_output_tokens,
"safety_margin": safety_margin,
"reserved_total_tokens": effective_input + max_output_tokens + safety_margin,
"available_input_tokens": max(0, available - overhead_tokens),
}
def validate_response(
output_tokens: int,
*,
max_output_tokens: int,
finish_reason: str | None = None,
) -> dict[str, int | str | None]:
"""Validate the completion actually returned by a provider.
The preflight check protects the request, while this postflight check
catches SDKs that ignore or reinterpret ``max_output_tokens``. A length
finish is reported as truncation so callers can persist a checkpoint and
continue with a fresh bounded request instead of replaying the old one.
"""
try:
actual = int(output_tokens)
limit = int(max_output_tokens)
except (TypeError, ValueError) as exc:
raise ModelBudgetError("response token counts must be integers") from exc
if actual < 0 or limit < 0:
raise ModelBudgetError("response token counts must be non-negative")
if actual > limit:
raise ModelBudgetError(
"model response exceeded output token budget "
f"(actual={actual}, limit={limit})"
)
return {
"output_tokens": actual,
"max_output_tokens": limit,
"finish_reason": finish_reason,
"truncated": finish_reason in {
"length",
"max_tokens",
"max_output_tokens",
"max_completion_tokens",
},
}
def validate_provider_response(
response: Any,
*,
max_output_tokens: int,
) -> dict[str, int | str | None | bool]:
"""Validate usage and completion state returned by a provider.
``response`` may be a normal Responses API object or a streaming event
envelope. Failed/incomplete responses are rejected as non-reusable so a
caller cannot append partial output to the next request. A length-limit
completion is returned as ``truncated`` and must be resumed from a fresh
checkpoint.
"""
response_object = _response_object(response)
usage = extract_response_usage(response_object)
status = response_object.get("status")
incomplete = response_object.get("incomplete_details")
finish_reason = None
if isinstance(incomplete, dict):
reason = incomplete.get("reason")
if reason is not None:
finish_reason = str(reason)
if finish_reason is None and response_object.get("finish_reason") is not None:
finish_reason = str(response_object["finish_reason"])
if status is None and finish_reason is None:
raise ModelBudgetError("provider response is missing completion status")
result = validate_response(
usage["output_tokens"],
max_output_tokens=max_output_tokens,
finish_reason=finish_reason,
)
if status in {"failed", "cancelled"}:
raise ModelBudgetError(
f"provider response is not reusable (status={status})"
)
if status == "incomplete" and not result["truncated"]:
raise ModelBudgetError(
"provider response is incomplete without a length checkpoint reason"
)
result.update(usage)
result["status"] = str(status) if status is not None else None
return result
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("packet", type=Path)
parser.add_argument("--context-window", type=int, default=DEFAULT_CONTEXT_WINDOW)
parser.add_argument("--max-output-tokens", type=int)
parser.add_argument("--safety-margin", type=int, default=DEFAULT_SAFETY_MARGIN)
parser.add_argument("--input-tokens", type=int, help="exact count from the provider tokenizer")
parser.add_argument(
"--overhead-tokens",
type=int,
required=True,
help="system/tool/history tokens outside the context packet",
)
args = parser.parse_args()
try:
with args.packet.open("r", encoding="utf-8") as handle:
packet = json.load(handle)
if not isinstance(packet, dict):
raise ModelBudgetError("packet must be a JSON object")
result = validate_request(
packet,
context_window=args.context_window,
max_output_tokens=args.max_output_tokens,
safety_margin=args.safety_margin,
input_tokens=args.input_tokens,
overhead_tokens=args.overhead_tokens,
)
except (OSError, UnicodeError, json.JSONDecodeError, ModelBudgetError) as exc:
parser.error(str(exc))
print(
"model_budget=valid "
f"input_tokens={result['input_token_limit']} "
f"estimated_input={result['estimated_input_tokens']} "
f"overhead={result['overhead_tokens']} "
f"max_output={result['max_output_tokens']} "
f"reserved_total={result['reserved_total_tokens']}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""Validate the small task pointer loaded at the start of a work session."""
from __future__ import annotations
import argparse
import json
import re
from pathlib import Path
MAX_POINTER_BYTES = 8192
MAX_POINTER_LINES = 180
REQUIRED_HEADINGS = ("## Current", "## Read", "## Task", "## Next", "## Verify", "## Stop")
FORBIDDEN_HEADINGS = (
"## 6. 已验证证据",
"## Evidence",
"## History",
"## 历史",
"## Chat",
)
class PointerError(ValueError):
"""Raised when the bootstrap pointer would expand the model context."""
def validate_pointer(
path: Path,
*,
max_bytes: int = MAX_POINTER_BYTES,
max_lines: int = MAX_POINTER_LINES,
lock_path: Path | None = None,
) -> dict[str, int]:
text = path.read_text(encoding="utf-8")
size = len(text.encode("utf-8"))
lines = len(text.splitlines())
if size > max_bytes:
raise PointerError(f"task pointer exceeds byte budget ({size}>{max_bytes})")
if lines > max_lines:
raise PointerError(f"task pointer exceeds line budget ({lines}>{max_lines})")
missing = [heading for heading in REQUIRED_HEADINGS if heading not in text]
if missing:
raise PointerError(f"task pointer missing sections: {', '.join(missing)}")
found = [heading for heading in FORBIDDEN_HEADINGS if re.search(rf"^\s*{re.escape(heading)}\s*$", text, re.MULTILINE)]
if found:
raise PointerError(f"task pointer contains history section: {found[0]}")
if "coverage/" not in text:
raise PointerError("task pointer must direct large evidence to paths")
pointer_match = re.search(r"^context_id:\s*([^\s]+)\s*$", text, re.MULTILINE)
if pointer_match is None:
raise PointerError("task pointer is missing context_id")
if lock_path is not None and lock_path.exists():
try:
lock = json.loads(lock_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise PointerError(f"cannot read context lock: {exc}") from exc
lock_id = lock.get("context_id") if isinstance(lock, dict) else None
if not lock_id:
raise PointerError("context lock is missing context_id")
if pointer_match.group(1) != str(lock_id):
raise PointerError(
f"task pointer context_id does not match lock ({pointer_match.group(1)}!={lock_id})"
)
return {"bytes": size, "lines": lines, "max_bytes": max_bytes, "max_lines": max_lines}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("path", nargs="?", type=Path, default=Path("nextTask.md"))
parser.add_argument("--max-bytes", type=int, default=MAX_POINTER_BYTES)
parser.add_argument("--max-lines", type=int, default=MAX_POINTER_LINES)
parser.add_argument("--lock", type=Path, default=Path("context-lock.json"))
args = parser.parse_args()
try:
result = validate_pointer(
args.path,
max_bytes=args.max_bytes,
max_lines=args.max_lines,
lock_path=args.lock,
)
except (OSError, UnicodeError, PointerError) as exc:
parser.error(str(exc))
print(f"task_pointer=valid bytes={result['bytes']} lines={result['lines']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())