Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning

### Changed

- Release codesign now preserves an existing valid ad-hoc signature and refuses publication when
post-install signing changes the retained wheel's native bytes, so the published wheel and the
runtime exercised by conformance and final examples are byte-identical.
- Strict AMR checkpoint payload v7 now persists the accepted shared-interface flux audit together
with Program clocks, histories, tagging state, conservative ledger and synchronization report.
Restart validates every fragment's topology epoch, level pair, exact clock window, resolved
Expand Down Expand Up @@ -116,6 +119,8 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning
native ABI, and checkpoint envelopes independently in one generated Python/C++ release contract;
declares the exact source/wheel matrix; and adds a fail-closed release preflight requiring exact
tag, installed native identity, clean tree, generated products, and authenticated final-gate evidence.
The retained wheel filename and its internal `WHEEL` record must also identify exactly the promised
native macOS/arm64/cp312 lane; Python/ABI, platform, purelib, build-tag, or metadata drift is refused.
- ADC-633 Compiled condensed-implicit time Program (std.condensed_schur, theta=1) on the AMR hierarchy: the condensed operators run per level through AmrProgramContext::grid_context / assembly_target / assembly_source (matrix-free coefficiented apply, reconstruct, energy), so a flat hierarchy is bit-identical to the uniform Program (the emitted matrix-free BiCGStab runs on level 0 through ctx.solve_linear_matfree) and a refined hierarchy solves the tensor elliptic by the composite FAC (CompositeFacPoisson in amr_condensed_elliptic.hpp), matching the native source-stage route; the vestigial AMR deferral stubs are removed and the Spec 6 sec.20 clean_schur_program.amr.mono cell flips to green.
- ADC-640 One spatial-reconstruction dispatch generator (include/pops/runtime/builders/scheme_dispatch.hpp): dispatch_limiter binds a typed LimiterRouteId to its compile-time reconstruction policy behind an X-macro plus a count-lock static_assert, so the 17 hand-written limiter ladders across the System, polar, AMR multi-block, AMR compiled and external-brick builders collapse to one dispatch_limiter call each. A forgotten limiter is now a build error (the -Werror-free tree could only warn on a missing switch arm). Same template instantiations, bit-identical.
- ADC-637 condensed_schur gained a generic lowering route: the electrostatic-Lorentz linearization J = [[0, B_z], [-B_z, 0]] is authored in the DSL (pops.lib.physics.author_electrostatic_lorentz, an m.local_linear_map on the momentum subset) and the macro (route="generic") lowers the condensed tensor coefficient A = I + c*rho*(I - theta*dt*J)^-1, the fused RHS and the velocity reconstruction through the closed-form block_inverse codegen, with no coupling/schur vocabulary. Bit-identical to the retiring hand-written Schur brick over a multi-step trajectory at theta == 1 and theta == 0.5 (golden, np.array_equal): the coefficient tensor reuses block_inverse<2> (== LorentzEliminator's binv entries) and the flux/reconstruct vector applies reuse a new factored block_apply_inverse intrinsic reproducing apply_Binv's operation order. The brick route stays the default until it is retired.
Expand Down
4 changes: 4 additions & 0 deletions docs/VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,9 @@ before the official build begins.
`## [x.y.z] - YYYY-MM-DD` section.
3. Run `python scripts/generate_release_contract.py --check` and the release preflight; a missing
build/codesign/example/conformance evidence record blocks tagging.
The Darwin gate first preserves an already-valid ad-hoc signature and requires the post-codesign
native digest to remain byte-identical to the retained wheel member. A repair confined to the
installed copy therefore blocks publication: the wheel users receive must itself contain the
exact signed runtime exercised by conformance and the final examples.
4. Merge, then `git tag vx.y.z` on master and `git push --tags`. The `release.yml` workflow
turns the tag into a GitHub Release built from that CHANGELOG section.
62 changes: 60 additions & 2 deletions scripts/codesign_pops_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
from __future__ import annotations

import argparse
from collections.abc import Sequence
import hashlib
import importlib.machinery
import importlib.util
import json
from pathlib import Path
import shutil
import subprocess
import sys
from typing import Sequence
from typing import Any


CODESIGN_EVIDENCE_SCHEMA_VERSION = 1


class CodesignError(RuntimeError):
Expand Down Expand Up @@ -66,6 +72,33 @@ def _checked_codesign(command: Sequence[str], *, action: str) -> subprocess.Comp
return result


def _has_valid_adhoc_signature(codesign: str, extension: Path) -> bool:
"""Return whether ``extension`` already carries the release signature policy.

Release validation must not rewrite bytes which came from the retained wheel: those are the
bytes eventually published. Probe first and only repair an absent/invalid signature. The
release preflight separately refuses a repair which changes the retained native-member digest.
"""

verification = subprocess.run(
(codesign, "--verify", "--strict", "--verbose=2", str(extension)),
text=True,
capture_output=True,
check=False,
)
if verification.returncode != 0:
return False
inspection = subprocess.run(
(codesign, "--display", "--verbose=4", str(extension)),
text=True,
capture_output=True,
check=False,
)
if inspection.returncode != 0:
return False
return "Signature=adhoc" in "%s\n%s" % (inspection.stdout, inspection.stderr)


def codesign_imported_extensions(*, if_present: bool = False) -> tuple[Path, ...]:
"""Sign and verify every extension a clean ``import pops`` will load on Darwin."""
if sys.platform != "darwin":
Expand All @@ -80,6 +113,8 @@ def codesign_imported_extensions(*, if_present: bool = False) -> tuple[Path, ...
if not codesign:
raise CodesignError("Darwin requires 'codesign', but it is not available on PATH")
for extension in extensions:
if _has_valid_adhoc_signature(codesign, extension):
continue
_checked_codesign(
(codesign, "--force", "--sign", "-", str(extension)),
action="ad-hoc signing %s" % extension)
Expand All @@ -97,17 +132,40 @@ def codesign_imported_extensions(*, if_present: bool = False) -> tuple[Path, ...
return extensions


def codesign_evidence(extensions: Sequence[Path]) -> dict[str, Any]:
"""Describe the exact post-sign extension bytes authenticated by this process."""
return {
"schema_version": CODESIGN_EVIDENCE_SCHEMA_VERSION,
"platform": sys.platform,
"extensions": [
{
"path": str(extension.resolve()),
"sha256": hashlib.sha256(extension.read_bytes()).hexdigest(),
"signature": "adhoc",
}
for extension in extensions
],
}


def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--if-present", action="store_true",
help="skip only when the pops package is absent (a present package without _pops fails)")
parser.add_argument(
"--json", action="store_true",
help="print machine-authenticated post-sign paths and hashes")
args = parser.parse_args(argv)
try:
extensions = codesign_imported_extensions(if_present=args.if_present)
except CodesignError as error:
evidence = codesign_evidence(extensions)
except (CodesignError, OSError) as error:
print("ERROR: %s" % error, file=sys.stderr)
return 1
if args.json:
print(json.dumps(evidence, sort_keys=True))
return 0
if sys.platform == "darwin":
if extensions:
for extension in extensions:
Expand Down
3 changes: 2 additions & 1 deletion scripts/final_release_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@
PYTHON_REQUIRED_SELECTION = "not mpi and not hdf5"
REQUIRED_RELEASE_GATES = (
"official_build",
"doctor",
"installed_wheel",
"codesign",
"doctor",
"native_conformance",
"python_conformance",
"examples",
Expand Down
Loading
Loading