ADR-0007: Release engineering — version SSOT, rc-first, the registry ripple, JPL¶
Status: ✅ Accepted (standing architecture policy; consolidates discipline previously held only in project memory). Date: 2026-07-17. Authors: Steven Kirkland + Claude. Supersedes: none. Superseded-by: none.
1. Context¶
srmech (and its sister packages) publish continuously. The recurring failure modes are mechanical and cheap to prevent with a fixed procedure: a version bumped in some but not all SSOT locations; a new public callable that trips CI one hidden gate at a time; a clean tag pushed by hand that should have been auto-tagged; a "fast test" CI that misses a build-env error. This ADR fixes the mechanical release procedure. (Standing authorizations — autonomous rc-merge, no human gate between rcs — live in project memory, not here; this ADR is the mechanics.)
2. Decision¶
-
Version SSOT is FIVE locations, changed together.
python/pyproject.toml,python/pyproject-pure.toml,python/srmech/version.py,c/include/srmech.h(bothSRMECH_VERSION_PRE+SRMECH_VERSION), and the version-pin inpython/tests/test_signal_processing_scaffolding.py. After a bump, rebuild the native.so(a stale.sofails version/parser-version parity locally only — CI builds fresh). A genome/format change also bumps the format version + the C rule preimages in lockstep. -
rc-first to TestPyPI; clean tags auto-publish to production. Every release ships
vX.Y.ZrcNto TestPyPI first (autotag SKIPS pre-release/non-strict-semver tags — push the rc tag by hand on the FEATURE branch/merge commit). Only a clean (non-rc) strict-semver tag routes to production PyPI, and clean tags are AUTO-tagged on merge — never pushed by hand. Verify in a clean venv OUTSIDE the source tree (source-tree namespace shadowing loads_native.pywithHAS_NATIVE=Falsespuriously). -
A new PUBLIC
srmech.amsc.*callable trips a fixed ripple — run it ALL locally before pushing (rc258 burned 3 CI cycles finding these one at a time): (a) atool_schema.pyToolEntry; (b) MCP coercion — every param TYPE STRING must have a coercer insrmech.mcp._coercion(reusedict/Sequence[tuple]etc., never a novel type string); © arosetta_classification.ndjsonbucket line; (d) regenerate_tool_docs.py; (e) thetools.totalcount across ~46 test files; (f) regenerate BOTH C registries (srmech_carrier_registry.c+srmech_tool_registry.c, UTF-8/LF) and rebuild the.soto verify byte-parity. A callable that merely composes registered ops may be_EXEMPT+ a rosetta line only (2 gates, not 6). -
Merge policy. Never squash-merge (
gh pr merge --merge/--rebase). At a partition boundary, update the rolling PR description. -
CI shape + local pre-checks. Per-PR CI is LEAN fast tests + a pedantic C matrix (Linux gcc / macOS clang / Windows MSVC,
-Werror//WX) + a pure-wheel build; the rc→TestPyPI step is what catches build-env errors the fast tests miss. A local WSL C smoke MUST build RELEASE (-O2 -DNDEBUG, pedantic) to reproduce CI. In a native-absent dev env, native-presence / single-C-call checks skip (not fail) via@pytest.mark.skipif(not _native.HAS_NATIVE)(#843, fixed) — CI always builds the native lib fresh, so those checks always run + assert there; the skip only affects a native-off dev run. -
Platform-agnostic by construction — machine-specific code routes ONLY through the HAL or the PAL; the core C is portable. Target-specific optimizations (SIMD / SHA-NI / intrinsics) go behind the HAL (
srmech_simd.h, the optimize path) so no machine-specific code leaks into a kernel — its target magic constants are attested in the header and derive-and-assert. All I/O (file / stream / socket — e.g. Windows named pipes vs POSIX) goes behind the PAL (srmech_platform*.c; the everything-mirrors-through-PAL rule is ADR-0003 point 4). The pedantic 3-OS matrix (Linux gcc / macOS clang / Windows MSVC,-Werror//WX) is the PROOF that no target-specific code escaped the HAL/PAL — a new warning on any OS fails CI. -
Full immolation before a production graduation. Before a clean (non-rc) PyPI publish, exercise every class/function touched through its TOML + surface in a clean environment.
-
A float/FPU-derived parity test is within-tolerance, NEVER a cross-platform SHA. Only exact-integer ops get a byte-identical cross-platform digest.
3. Consequences¶
- Version drift, hidden-gate CI churn, and hand-pushed clean tags are structurally prevented.
- The registry-ripple checklist is the single reference for "I added a public op".
4. Sources (consolidated from project memory)¶
[[feedback_srmech_version_bump_hits_five_locations_run_suite_after]] ·
[[feedback_new_amsc_public_callable_registry_checklist]] ·
[[feedback_new_tool_entry_needs_mcp_coercer_and_smoke_sample]] ·
[[feedback_new_tool_ripple_full_checklist]] ·
[[feedback_always_rc_first_for_downstream_publishes]] ·
[[feedback_clean_release_tags_are_autotagged_never_manual]] ·
[[feedback_rc_tag_targets_branch_not_merge]] · [[feedback_no_squash_merges]] ·
[[feedback_rolling_pr_partition_boundary_updates]] ·
[[feedback_lean_test_ci_testpypi_catches_build_env_errors]] ·
[[feedback_wsl_smoke_must_build_release_ndebug_to_match_ci_pedantic]] ·
[[feedback_native_absent_dev_env_stale_dll_and_immolation_hang]] ·
[[feedback_full_immolation_before_live_publish]] ·
[[feedback_numeric_parity_test_within_tol_not_cross_platform_sha]] ·
[[feedback_no_ship_known_broken_gold_is_law]]
JPL Power-of-Ten (C): the srmech C library passes all 10 Holzmann rules
(docs/srmech/c/JPL_AUDIT.md); tests/test_jpl_audit.py is a mechanical ratchet
(no-goto / no-malloc / ≤60-line functions / ≥2 asserts per non-exempt function /
no multi-line macros) — violations only go DOWN. Struct fields are ordered
big-first (8→4→2→1 byte) to minimise padding. (The HAL/PAL platform-abstraction that
keeps machine-specific code out of the kernels is decision point 6 above.)
([[feedback_jpl_rule_5_two_assert_habit]] ·
[[feedback_struct_field_ordering_big_first]] ·
[[feedback_simd_optimize_path_goes_through_hal]] ·
[[feedback_hal_magic_constants_attested_and_derived]])